Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Four Ways to Implement Lazy Loading in C#

0.00/5 (No votes)
15 Jul 2013 1  
Analyzing the Lazy Loading design pattern.

Introduction

Implementing the Lazy Loading design pattern in your application comes in handy when you're dealing with large quantities of objects.

Background

Let's imagine that some data is provided to you by a database or by a service, and let's suppose that you're going to work with objects that have a huge amount of related objects.

Loading all of them in one fell swoop when your application starts may significantly hurt performances. What if you could load those objects only when you actually need them? That's what Lazy Loading is for.

Using the Code

There are four main ways through which you can implement the Lazy Loading pattern:

  • Lazy Initialization
  • Virtual Proxies
  • Value Holders
  • Ghost Objects

Lazy Initialization

The Lazy Initialization technique consists of checking the value of a class' field when it's being accessed: if that value equals null, the field gets loaded with the proper value before it is returned. Here is an example:

public class Customer
{
    public int CustomerID { get; set; }
    public string Name { get; set; }
    private IEnumerable<Order> _orders;

    public IEnumerable<Order> Orders
    {
        get
        {
            if (_orders == null)
            {
                _orders = OrdersDatabase.GetOrders(CustomerID);
            }
            return _orders;
        }
    }

    // Constructor
    public Customer(int id, string name)
    {
        CustomerID = id;
        Name = name;
    }
} 

Pretty simple, isn't it? However, there's actually an alternative method through which we can accomplish the same goal.

The .NET Framework defines a class named Lazy<T>, which itself incorporates the Lazy Loading behaviour. The Lazy<T> class needs two basic information regarding the object that's going to be "lazily" initialized:

  • Its type (specified in the type parameter)
  • How to load its value (i.e. which function should be called)

Using the parameterless constructor, the operations performed when the Value property of the Lazy<T> class is being accessed are the ones contained in the default constructor of the object whose type is specified in the "T" parameter. Here's an example below:

Lazy<IEnumerable<Order>> lazyOrders = new Lazy<IEnumerable<Order>>();
IEnumerable<Order> actualOrders = lazyOrders.Value;

The default constructor for the Order class is invoked, and actualOrders gets filled.

If we wanted to invoke a custom function, instead, we would simply pass that same function in the constructor. That's exactly what I did in my example:

Lazy<IEnumerable<Order>> lazyOrders = new Lazy<IEnumerable<Order>>(MyFunction);
IEnumerable<Order> actualOrders = lazyOrders.Value; 

Virtual Proxy

A Virtual Proxy represents an object that is quite similar to the one that we wish to lazily initialize (they implement the same interface), yet substantially different. The proxy object, indeed, is actually empty. When the user tries to access the proxy object for the first time, the Virtual Proxy creates a new instance of the real object and properly fills it (this can be seen as an On Demand initialization).

One of the disadvantages of this approach is that you may need to create lots of virtual proxies if you're going to proxy a large number of objects.

Note: The user is obviously unaware that they're dealing with the "proxy" object instead of with the "real" one.

Value Holder

Adopting a Value Holder, instead, you basically set up a sort of surrogate for the real object. When the user needs to access it, they simply ask the value holder for its value by calling the GetValue method. At that time (and only then), the value gets loaded from a database or from a service.

The main drawback of this approach is that the user has to know that a value holder is expected.

Here's an example definition of a value holder:

public class ValueHolder<T>
{
    private T _value;
    private readonly Func<object, T> _valueRetrieval;

    // Constructor
    public ValueHolder(Func<object, T> valueRetrieval)
    {
        _valueRetrieval = valueRetrieval;
    }

    // We'll use the signature "GetValue" for convention
    public T GetValue(object parameter)
    {
        if (_value == null)
            _value = _valueRetrieval(parameter);
        return _value;
    }
}  

Ghost Object

A Ghost Object corresponds to the real object, but not in its full state. It may be empty, or it may contain just some fields (such as the ID). When the user tries to access some fields that haven't been loaded yet, the ghost object fully initializes itself (this is not always needed).

History

  • 07-14-2013: First version.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here