X

Using Lazy<T> for lazy loading

.Net Framework 4.0 introduced a new class called Lazy<T>. Lazy<T> provides support for several common patterns of lazy initialization, including the ability to initialize value types and to use null values. It’s a powerful and very useful beast but underrated in practice. Here’s how to use it.

How Lazy<T> works

I wrote a little code example on Visual Studio to illustrates how to use Lazy<T>.

static void Main(string[] args)
{
    var lazyString = new Lazy<string>(() =>
    {
        // Here you can do some complex processing and then return a value.
        Console.Write("Inside lazy loader");
        return "Lazy loading!";
    });

    Console.Write("Is value created: ");
    Console.WriteLine(lazyString.IsValueCreated);

    Console.Write("Value: ");
    Console.WriteLine(lazyString.Value);

    Console.Write("Value again: ");
    Console.WriteLine(lazyString.Value);

    Console.Write("Is value created: ");
    Console.WriteLine(lazyString.IsValueCreated);

    Console.WriteLine("Press any key to continue ...");
    Console.ReadLine();
}

When we run this code we will get the following output.

    Is value created: False
Inside lazy loader Value: Lazy loading!
Value again: Lazy loading! Is value created: True Press any key to continue …

The value of Lazy<string> will be initialized when we first ask it and then it will be stored for subsequent calls. Notice that there is one Console.WriteLine inside lazy initialization function and if you look at output you can see that this function is run only once. So only thing you have to do is to write initialization function and Lazy<T> makes all the work automatically.

NB! Lazy<T> provides thread-safety when initializing a value if developers specify thread-safety mode. Later use of loaded value is not thread-safe by Lazy<T> and it’s up to calling code to lock while using a value.

Example: Lazy singleton

One of the most popular examples of Lazy<T> is lazy initialization of singleton instance.

public class Singleton
{
    private static readonly Lazy<Singleton> _instance;

    private Singleton() { }

    static Singleton()
    {
        _instance = new Lazy<Singleton>( () => new Singleton() );
    }

    public static Singleton Instance
    {
        get
        {
            return _instance.Value;
        }
    }

    public void DoSomething()
    {
    }
}

Those who prefer shorter code can leave out static constructor and write same class like show here.

public class Singleton
{
    private static readonly Lazy<Singleton> _instance =
                new Lazy<Singleton>(() => new Singleton());

    private Singleton() { }

    public static Singleton Instance
    {
        get
        {
            return _instance.Value;
        }
    }

    public void DoSomething()
    {
    }
}

Design advice. Don’t show Lazy<T> to consumers of your class. It’s better to handle it as an internal implementation detail of class.

Lazy<T> is not a same as returning value with null check

There’s one misconception about Lazy<T> and one can easily find many discussions where Lazy<T> is made equal to code like this.

private static Singleton _instance;

// Not a synonym of Lazy<T>
public static Singleton Instance
{
    get
    {
        if(_instance == null)
        {
            _instance = new Singleton();
        }

        return _instance;
    }
}

Loading a value is just one part of Lazy<T> but there are few more things that this code doesn’t cover:

  1. Thread-safety while value is loading
  2. Knowing if value was loaded

Bug alert! The code above with null check is not even lazy loading but really bad bug. Do you see what’s wrong?

To completely destroy the code with null check I would like to point out one easy to miss detail: initialized value can be null. Take a careful look at the following code.

private static Singleton _instance;

public static Singleton Instance
{
    get
    {
        if (_instance == null)
        {
            _instance = DoComplexInitialization();
        }

        return _instance;
    }
}

private static Singleton DoComplexInitialization()
{
    return null;
}

Every time when client code asks for instance the initializion method DoComplexInitialization() is called. It doesn’t happen with Lazy<T>.

public class Singleton
{
    private static readonly Lazy<Singleton> _instance;

    private Singleton() { }

    static Singleton()
    {
        _instance = new Lazy<Singleton>(() => {
            Console.WriteLine("Loading value");

            return null;
        });
    }

    public static Singleton Instance
    {
        get
        {
            return _instance.Value;
        }
    }

    public void DoSomething()
    {
    }
}

If calling code asks instance twice then “Loading value” is written to console only once.

What if initialization fails?

It’s possible to get exception during initialization of lazy loaded value and this is the point where we want to decide if initialization code should run again when asking for value or not.

Let’s modify our Singleton class to make some experiments.

public class Singleton
{
    private static int i = 0;
    private static readonly Lazy<Singleton> _instance;

    private Singleton() { }

    static Singleton()
    {
        _instance = new Lazy<Singleton>(() => {
            Console.WriteLine("Loading value");

            i++;

            if (i == 1)
            {
                throw new Exception("Exception in Lazy<T>");
            }

            return new Singleton();
        });
    }

    public static Singleton Instance
    {
        get
        {
            try
            {
                return _instance.Value;
            }
            catch
            {
                return null;
            }
            finally
            {
                Console.Write("Is value loaded: ");
                Console.WriteLine(_instance.IsValueCreated);
            }
        }
    }

    public void DoSomething()
    {
    }
}

On first initialization the code above throws exception. On subsequent calls it returns instance of Singleton class.

Calling Instance property twice generates the following output.

Loading value
Is value loaded: False
Is value loaded: False

We can use LazyThreadSafetyMode enum to control how Lazy<T> handles threading and exceptions. If we use PublicationOnly value from enum then things change.

static Singleton()
{
    _instance = new Lazy<Singleton>(() => {
        Console.WriteLine("Loading value");

        i++;

        if (i == 1)
        {
            throw new Exception("Exception in Lazy<T>");
        }

        return new Singleton();
    }, LazyThreadSafetyMode.PublicationOnly);
}

Now we get the following output when asking Singleton instance twice.

Loading value
Is value loaded: False
Loading value
Is value loaded: True

NB! Be very careful when using Lazy<T> from multiple threads and make sure you go through LazyThreadSafetyMode enum documentation before doing any decisions. Also make sure you know how Lazy<T> behaves with selected thread-safety mode in race conditions.

Where else I can use Lazy<T>?

Lazy<T> is not just for singletons and code in static scope. Lazy<T> works well also in instance scope and we can also use it in script style code when writing utilities to make our work easier.

Here is the example of small nightly batch to process online invoices. It uses Lazy<PaymentProcessor> to lazy load payment processor that is expensive to initialize.

static void Main(string[] args)
{
    var lazyPaymentProcessor = new Lazy<PaymentProcessor>(() =>
    {
        var authKey = "<authentication key>";
        var secret = "<authentication secret>";

        var processor = new PaymentProcessor(authKey, secret);
        processor.LoadConfiguration();
        processor.Initialize();

        return processor;
    });

    var json = File.ReadAllText("invoices-import.json");
    var invoices = JsonConvert.DeserializeObject<List<Invoice>>(json);

    foreach(var invoice in invoices)
    {
        if(invoice.IsCreditInvoice)
        {
            lazyPaymentProcessor.Value.Process(invoice);
        }

        ReportToAccounting(invoice);
    }
}

Those who are interested in more challenging topics can read my post Using Lazy and abstract wrapper class to lazy-load complex system parameters. There’s a discussion about how to hide lazy loaded parameters to wrapper classes and keep code cleaner.

NB! It can be tempting to use Lazy<T> on domain classes in parallel with ORM (Object Relational Mapper) like Entity Framework or NHibernate. Don’t do it. These tools can usually handle all complex issues automatically.

Wrapping up

Although Lazy<T> seems like a small and kind of pointless utility class at first, it turns out to be a really useful beast when we dig deeper. I like Lazy<T> because I can use it over the whole solution to make sure that lazy loading follows the same pattern. We can use Lazy<T> for object and value types. It’s not only for big solutions – we can use Lazy<T> in small utility applications too if we need to initialize complex objects.

Liked this post? Empower your friends by sharing it!
Categories: .NET C#

View Comments (3)

  • I do not think I like this new feature. Lazy loading should be an intrinsic feature of a property. IE, the user of your API should not have to call ".value" or even know that the propery is returning a generic lazy type. Why couldn't they have implemented this as a language (keyword) feature instead of a CLR type which could be handled by the compiler instead?

  • @Jeremy Rabalais - You make an interesting point, but there's nothing to stop you using this implementation without compromising your API, such as by putting a property accessor around access to the lazy value. I think using System.Lazy in this manner in the internal implementation makes the intent of the code very clear, and reduces the opportunity to introduce poor lazy loading implementation or DRY violations.

  • your initialization that uses a null check is not thread-safe. It should be using double-check locking with the 'volatile' keyword declared on the field declaration.

    For a thread-safe implementation you need one of the following implementations:
    1. a static constructor for initialization
    2. inline static initialization at declaration time
    3. double-check locking with the 'volatile' keyword on the instance field decl.

Related Post