Inner Fields and Lazy Initialization in C#

Posted by: Steven Smith, on 29 Oct 2011 | View original | Bookmarked: 0 time(s)

Using lazy initialization in C#, a classs state is set up such that each propertys get method performs a check to see if the underlying field is null.  If it is, then it calculates or populates the field before returning it.  This is a very simple and common approach, but it requires that the class follows a convention of only accessing the field via the property.  Unfortunately, there are no language features that can enforce this, so its possible for errors to creep in.  Heres an example of this approach working correctly:

public class Order
{
    // other properties
 
    private Customer _customer;
    public Customer Customer
    {
        get
        {
            if (_customer == null)
            {
                _customer = new Customer();
            }
            return _customer;
        }
    }
 
    public string PrintLabel()
    {
        return Customer.CompanyName + "\n" + Customer.Address;
    }
}

Now heres where this approach can break down.  Consider the same class as above, but with a rewritten PrintLabel() method:

public string PrintLabel()
{
  return _customer.CompanyName + "\n" + _customer.Address;
}

This code will still compile just fine, but now will very likely result in a NullReferenceException when it attempts to access properties of the _customer, which may not yet be initialized.  The solution to this would be to control access to the _customer member.  Weve already set its access to private, though, which is as restrictive as we can make it.  We could force it to be initialized by moving the work into the classs constructor, but then were losing the benefits of lazy initialization.  I wonder if it wouldnt be useful to do something like this instead:

public class Order
{
    // other properties
 
    private Customer _customer;
    public Customer Customer
    {
        get
        {
            if (_customer == null)
            {
                _customer = new Customer();
            }
            return _customer;
        }
    }
 
    public string PrintLabel()
    {
        string result = _customer.CompanyName; // probably results in a NullReferenceException
        return result + "\n" + Customer.Address; // ok to access Customer
    }
}

One approach that can be used with the relatively new Lazy<T> type is this one (thanks to Jose Romanie for pointing this out):

public class Order
{
    public Order()
    {
        _customerInitializer = new Lazy<Customer>(() => new Customer());
    }
 
    // other properties
 
    private Lazy<Customer> _customerInitializer;
    public Customer Customer
    {
        get
        {
            return _customerInitializer.Value;
        }
    }
 
    public string PrintLabel()
    {
        string result = Customer.CompanyName; // ok to access Customer
        return result + "\n" + _customerInitializer.Value.Address; // ok to access via .Value
    }
}

I like this approach, and Im generally a fan of Lazy<T>.  It might eliminate the need for the private backing field idea for properties, as it does provide a means of enforcing the initialization even if the backing field is accessed from within the class.  The only downside is that you need to work with a Lazy<T> instead of a T, but within the class its probably not a bad thing for this detail to be exposed.  Thoughts? 


Advertisement
Free Agile Project Management Tool from Telerik
TeamPulse Community Edition helps your team effectively capture requirements, manage project plans, assign and track work, and most importantly, be continually connected with each other.
Category: C# | Other Posts: View all posts by this blogger | Report as irrelevant | View bloggers stats | Views: 117 | Hits: 3

News Categories

.NET | Agile | Ajax | Architecture | ASP.NET | BizTalk | C# | Certification | Data | DataGrid | DataSet | Debugger | DotNetNuke | Events | GridView | IIS | Indigo | JavaScript | Mobile | Mono | Patterns and Practices | Performance | Podcast | Refactor | Regex | Security | Sharepoint | Silverlight | Smart Client Applications | Software | SQL | VB.NET | Visual Studio | W3 | WCF | WinFx | WPF | WSE | XAML | XLinq | XML | XSD