views:

171

answers:

4

.NET 4.0 has a nice utility class called System.Lazy that does lazy object initialization. I would like to use this class for a 3.5 project. One time I saw an implementation somewhere in a stackoverflow answer but I can't find it anymore. Does someone have an alternative implementation of Lazy? It doesn't need all the thread safety features of the framework 4.0 version.

Updated:

Answers contain a non thread safe and a thread safe version.

+6  A: 

If you don't need thread-safety, it's pretty easy to put one together with a factory method. I use one very similar to the following:

public class Lazy<T>
{
    private readonly Func<T> initializer;
    private bool isValueCreated;
    private T value;

    public Lazy(Func<T> initializer)
    {
        if (initializer == null)
            throw new ArgumentNullException("initializer");
        this.initializer = initializer;
    }

    public bool IsValueCreated
    {
        get { return isValueCreated; }
    }

    public T Value
    {
        get
        {
            if (!isValueCreated)
            {
                value = initializer();
                isValueCreated = true;
            }
            return value;
        }
    }
}
Aaronaught
Anyone who might copy this: It can be easy to have closure confusion with the initializer here. Make sure to capture your values!
Rex M
@Rex: Do you mean, if you're initializing the `Lazy<T>` instance from a loop? Does `System.Lazy` do some magic to sidestep the normal dangers of capturing?
Aaronaught
@Aaronaught a loop is a good example. I doubt if the real `Lazy` is any different, though I am not sure. Just thought I'd point it out because I see a lot of people get themselves in trouble with this kind of pattern.
Rex M
@Rex: Alrighty, just wanted to make sure you were referring to a usage scenario and not a design flaw that I failed to recognize.
Aaronaught
+1  A: 

A somewhat simplify version of aaron's

public class Lazy<T> where T : new()
{ 
  private T value; 

  public bool IsValueCreated { get; private set;}

  public T Value 
  { 
    get 
    { 
        if (!IsValueCreated) 
        { 
            value = new T();
            IsValueCreated = true; 
        } 
        return value; 
    } 
  } 
} 
James Curran
Requires default ctor
BC
@BC : Yes, which is what the ` where T : new() ` means.
James Curran
A: 

Some funny (but not very usable) stuff can be added: implicit coversion from delegate:

public static implicit operator Lazy<T>(Func<T> initializer)
{
    return new Lazy<T>(initializer);
}  

And usage

private static Lazy<int> Value = new Func<int>(() => 24 * 22);

C# compiler have some problem with performing this conversion, for example assigning lambda expression does not work, but it is one more thing causes your colleguas to think a bit :)

STO
lambdas can either be expression trees or delegates so the compiler refuse to consider them to be one or the other. same reason why you can't put a lambda in a var. Really annoying sometimes...
VirtualBlackFox
You are right, but this code does not work even with methods:`public static int NewValue() { return 24 * 15; }public static Lazy<int> V = NewValue; // Compilation Error, needs new Func<int>(NewValue)`
STO
+3  A: 

Here is an implementation that I use.

/// <summary>
/// Provides support for lazy initialization.
/// </summary>
/// <typeparam name="T">Specifies the type of object that is being lazily initialized.</typeparam>
public sealed class Lazy<T>
{
    private readonly object padlock = new object();
    private readonly Func<T> createValue;
    private bool isValueCreated;
    private T value;

    /// <summary>
    /// Gets the lazily initialized value of the current Lazy{T} instance.
    /// </summary>
    public T Value
    {
        get
        {
            if (!isValueCreated)
            {
                lock (padlock)
                {
                    if (!isValueCreated)
                    {
                        value = createValue();
                        isValueCreated = true;
                    }
                }
            }
            return value;
        }
    }

    /// <summary>
    /// Gets a value that indicates whether a value has been created for this Lazy{T} instance.
    /// </summary>
    public bool IsValueCreated
    {
        get
        {
            lock (padlock)
            {
                return isValueCreated;
            }
        }
    }


    /// <summary>
    /// Initializes a new instance of the Lazy{T} class.
    /// </summary>
    /// <param name="createValue">The delegate that produces the value when it is needed.</param>
    public Lazy(Func<T> createValue)
    {
        if (createValue == null) throw new ArgumentNullException("createValue");

        this.createValue = createValue;
    }


    /// <summary>
    /// Creates and returns a string representation of the Lazy{T}.Value.
    /// </summary>
    /// <returns>The string representation of the Lazy{T}.Value property.</returns>
    public override string ToString()
    {
        return Value.ToString();
    }
}
ChaosPandion
Two problems I have with this: First of all, it's preferable to `lock` a private object than to `lock (this)`, since you can't control who else might lock on your `Lazy` instance. Second, I don't think making `isValueCreated` a `volatile` field serves any purpose when you're already using a critical section (does it? Correct me if I'm wrong).
Aaronaught
I agree volatile is used when locking is not used. From MSDN: The volatile modifier is usually used for a field that is accessed by multiple threads without using the lock statement to serialize access. Using the volatile modifier ensures that one thread retrieves the most up-to-date value written by another thread.
BC
I modified the answer.
BC
@Aaronaught, @BC - I know this may sound stupid but imagine a situation where `isValueCreated` is read and written nearly at the same time. If `isValueCreated` is `volatile` then the extra lock will not need to be taken because the latest value was read. One simple lock may not seem like much but I did it for completeness sake. As for the `this` lock, I do see your point but it just never was an issue I ran into while using this class.
ChaosPandion