# Lazy properties without compromises in C#
Mar 1, 2018 4 minute readThe Lazy pattern is very helpful and widely adopted. However, most of the times its usage comes with compromises: the code looks ugly, it is not thread-safe, locks everywhere. The book Functional Programming in Java contains a great implementation of the Lazy pattern. This post is my attempt to explain the C# implementation.
TL;DR: Use my LazyPropertyHelper
nuget package for lazy properties. Read Functional Programming in Java.
These are two common attempts to implement the Lazy pattern. Each has its own benefits and drawbacks.
All code samples can be found here.
Attempt 1: Being Lazy and naive
Throughout my career I’ve written code like this. The code performs an expensive computation only when needed and caches the result to prevent unnecessary computations.
public class MyServiceNaive
{
private ExpensiveObject _expensiveLoad;
public ExpensiveObject ExpensiveLoad
{
get
{
if (_expensiveLoad == null)
{
_expensiveLoad = new ExpensiveObject();
}
return _expensiveLoad;
}
}
}
This code can be subject to weird race-conditions in a multi-threaded environment.
Attempt 2: Being Lazy and inefficient
The next approach is much better in terms of thread-safety. The expensive computation is executed only once.
public class MyLockedService
{
private object _criticalSection = new object();
private ExpensiveObject _expensiveLoad;
public ExpensiveObject ExpensiveLoad
{
get
{
lock (_criticalSection)
{
if (_expensiveLoad == null)
{
_expensiveLoad = new ExpensiveObject();
}
}
return _expensiveLoad;
}
}
}
This code has one major drawback: the lock gets acquired on every read. This behavior can greatly impact performance of multi-threaded applications.
Lazy properties without compromises
Wouldn’t it be nice if the two previous solutions could be merged? Implement a thread-safe lazy pattern where the lock is acquired only once, and subsequent reads are fast.
public class MyAwesomeService
{
private object _criticalSection = new object();
private Func<ExpensiveObject> _expensiveLoadReader;
public MyAwesomeService() => _expensiveLoadReader = createAndCacheExpensiveLoad;
public ExpensiveObject ExpensiveLoad => _expensiveLoadReader();
private class ExpensiveObjectFactory
{
private readonly ExpensiveObject _cachedResult = new ExpensiveObject();
public ExpensiveObject Build() => _cachedResult;
}
private ExpensiveObject createAndCacheExpensiveLoad()
{
lock (_criticalSection)
{
if (_expensiveLoadReader == createAndCacheExpensiveLoad)
{
_expensiveLoadReader = new ExpensiveObjectFactory().Build;
}
}
return _expensiveLoadReader();
}
}
That is certainly a lot of code. Let’s break it down:
ExpensiveLoad
is a property that returns whatever_expensiveLoadReader
returns_expensiveLoadReader
will callcreateAndCacheExpensiveLoad
by defaultcreateAndCacheExpensiveLoad
will replace_expensiveLoadReader
the first time it gets called. This replacement will be thread-safe- Subsequent calls to
_expensiveLoadReader
will return whateverExpensiveObjectFactory.Build
returns. These calls don’t need locks because they’re not mutating anything ExpensiveObjectFactory.Build
returns_cachedResult
_cachedResult
is only calculated once
This solution is certainly effective. And very complex too. Who would want to write all that code for a Lazy property?
LazyPropertyHelper
nuget to the rescue
All problems in computer science can be solved by another level of indirection. David Wheeler.
I created the LazyPropertyHelper
nuget to encapsulate the solution in a reusable way. It is open source, free, and ready to use.
public class MyServiceThatUsesLazyPropertyHelperNuget
{
private readonly Func<ExpensiveObject> _expensiveLoadReader =
LazyProperty.Create(() => new ExpensiveObject());
public ExpensiveObject ExpensiveLoad => _expensiveLoadReader();
}
The service code gets considerably simplified. Let’s break it down:
ExpensiveLoad
is still a property that returns whatever_expensiveLoadReader
returns_expensiveLoadReader
will create anew ExpensiveObject()
the first time it gets called. It will cache the result in a thread-safe manner- Subsequent calls to
_expensiveLoadReader
will always return the cached object without the need for locks
Summary
The complexities of implementing the Lazy pattern in a thread-safe an efficient manner can be abstracted out into a reusable library.