Lazy-Instantiation and the Lazy<T> – class

I’ve spent the last weeks playing around with MEF which uses a feature of .Net 4.0 I didn’t know before: the Lazy class. A class which gives us a standardized, easy to use and thread safe way for implementing lazy instantiation.

Lazy instantiation describes a procedure of creating class instances on demand e.g. when accessing a property. The advantage is that you only invest resources like CPU time and memory if it is really necessary.

In the past this was often realized by using the “null coalescing”-operator. This executes the right operant when ever the left one is null:

public class A
{
     private DataClass _data;
     public DataClass Data
     {
         get
         {
             return _data ?? (_data = new DataClass());
         }
     }
 }

The example with Lazy looks a bit different because we have to create an instance of this class first. The resulting object will than take care of the instantiation of our target type once the getter of its Value property is called.

public class A
{
   private Lazy<DataClass> _data = new Lazy<DataClass>();

   public DataClass Data
   {
        get
        {
            return _data.Value;
        }
    }
}

So far so good but it does not help us with the main disadvantage of the null coalescing operator because instantiation with Lazy is not thread safe by default. So, we have to add some parameters to the constructor call.

The easiest one is a true value which basically means that we want to have a thread safe instantiation. The following code snippet I gained with the Reflector shows what this really means.

public Lazy(bool isThreadSafe)
 : this(isThreadSafe ? LazyThreadSafetyMode.ExecutionAndPublication : LazyThreadSafetyMode.None)
{}

Nice, isn’t it? The boolean only defines which value of the LazyThreadSafetyMode-enumeration is used. I think I do not have to explain what none really does. More interesting is ExecutionAndPublication which defines that only the first thread is allowed to call the Value property and thus creates the instance. All others are locked until the execution has finished and will get the created instance.

ExecutionAndPublication can cause a lot of trouble on heavy use because the possibility of deadlocks rises with each dependency between the objects. That’s why we can find another entry in the enumeration called PublicationOnly.

PublicationOnly locks, like the name says, only the publication of the resulting instance. This means that all threads create an instance but only the first completely initialized one is published to all threads. So we reduce the amount of possible deadlocks but increase the effort by possibly creating multiple instances without any use.

One thing I did not speak about is the use of parameterized constructors of lazy instantiated objects. I did not mentioned it but objects need to have a constructor without any parameters by default. That’s obvious because how should Lazy know which constructor to choose? This is mostly a problem if you use some kind of dependency injection. In this case you must give all the dependencies to an object it needs either by constructor parameters or by setting the corresponding properties.

That’s why Lazy provides another constructor which takes a Function – delegate. This delegate is used to define how an object shall be initialized and will be called when ever the Value-Property is called the first time.

public class A
{
   private Lazy<DataClass> _data;

   // Constructor
   public A(ILogger logger)
   {
        _data = new Lazy<DataClass>( () => {

              // On access create instance with logger
              return new DataClass(logger);

        });
   }

   public DataClass Data
   {
        get
        {
            return _data.Value;
        }
    }
}

Now we got it. The function allows us for example accessing a data base, calling complex calculations and so on. Thanks to Lazy all this can be done very easily, thread safe and in a standardized manner.

Kommentar hinterlassen

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

2 × fünf =

Bitte folgende Aufgabe lösen um fortzufahren

Wieviel ist 10 + 9 ?
Please leave these two fields as-is: