Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lazy<T> implementation and .NET generics

I was looking for ways to do lazy initialization and found Lazy<T> which is included in .NET 4.

I was thinking of rolling my own implementation of Lazy<T> for .NET 3.5 (with a simpler multi-thread policy), and I bumped into the following problem:

Lazy has basically two types of constructors:

class Lazy<T> {

    public Lazy(){...} // ctor #1

which uses T's default constructor for creating an instance of T, and

    public Lazy(Func<T> func){...} // ctor #2

which lets the caller decide how the instance of T is created.

Now here's the problem:

If I want compile-time checking for the 1st ctor I will add a restriction

class Lazy<T> where T: new() {...}

at the class level. This will allow me to use new T() to create an instance; but this restriction is not necessary for the 2nd ctor, and worse, it also restricts the types I can use (to those with a default ctor)

If I want to be able to use any type with the 2nd ctor, I will not set any restriction, and in the 1st ctor will use reflection to make sure T does have a default ctor. This approach, however, will lack the compile-time check, and will only throw a runtime exception if the 1st ctor is used with the wrong type.

My question is: Can I get the best of both worlds?

Ideally, I would like to get the compile-time check for every use of ctor #1, but at the same time be able to use ctor #2 for types that don't have a default ctor.

How does the Microsoft implementation do this? (I don't readily have access to the .NET 4 sources or dlls).

EDIT: (After "Reflector-ing" the MS assembly)

I checked the reference implementation and it doesn't do compile-time checks.
It uses reflection for the 'default ctor' case, of course accompanied by the runtime exception if things go bad.

like image 425
Cristian Diaconescu Avatar asked Aug 11 '10 20:08

Cristian Diaconescu


People also ask

Can you explain lazy loading in C#?

Lazy Loading is a technique that delays the initialization of an object. This is a new feature of C# 4.0. The basic idea of lazy loading is to load objects or data only when they are needed. A lazy loading pattern is also called Object on Demand.

What is Lazy T in C#?

The Lazy<T> object ensures that all threads use the same instance of the lazily initialized object and discards the instances that are not used.

Is lazy initialization good practice?

Lazy initialization is an excellent performance optimization technique, allowing you to defer the initialization of objects that consume significant CPU and memory resources until you absolutely need them. Take advantage of lazy initialization to improve the performance of your apps.

How is lazy initialization implemented?

Implementing a Lazy-Initialized Property To implement a public property by using lazy initialization, define the backing field of the property as a Lazy<T>, and return the Value property from the get accessor of the property. The Value property is read-only; therefore, the property that exposes it has no set accessor.


3 Answers

I expect the inbuilt implementation simply uses Activator.CreateInstance<T> for simplicity. The cleanest way I can think of cheating this is with a separate factory:

// non-generic factory class with generic methods
public static class Lazy {
    public static Lazy<T> Create<T>() where T : new() {
        return Create<T>(() => new T());
    }
    public static Lazy<T> Create<T>(Func<T> ctor) { ... }
}
public class Lazy<T> { ... }
like image 138
Marc Gravell Avatar answered Nov 01 '22 05:11

Marc Gravell


You could use a static factory method instead of a overload to the constructor:

public class Lazy<T>
{
    public Lazy( Func<T> f ) { /*...*/ }

   public static Lazy<R> Default<R>() where R : T, new()
   {
       return new Lazy<R>( () => new R() );
   }
}

Now, this breaks compatibility (to some extent) with the .NET 4.0 version of Lazy<T>, but it does achieve compile time safety for both types of usage.

You could make this a bit cleaner by making the constructors for Lazy<T> protected internal, and provide a static factory class that you always use to create instances:

public static class Lazy {
    static Lazy<T> Create<T>( Func<T> ) { ... }
    static Lazy<T> Create<T>( ) where T : new() { ... }
}
like image 22
LBushkin Avatar answered Nov 01 '22 04:11

LBushkin


Why don't you just download the parallel extesions and install Lazy<T> for 3.5? Direct link

like image 2
µBio Avatar answered Nov 01 '22 03:11

µBio