Lazy<T> property caching alternative

Lazy, property cache must often be implemented in constructor

Lazy is a handy class, available since .NET 4.0. It has only one big disadvantage: if you use it to cache a property and it uses other members of the class, you need to implement the property in the constructor. An example:


      class Test
      {
          private readonly Lazy<int> _someValue =
              new Lazy<int>(GetSomeValue); // Does not compile
           
          public int SomeValue
          {
              get
              {
                  return _someValue.Value;
              }
          }
       
          private int GetSomeValue()
          {
              // Not static, calls other properties
              throw new NotImplementedException();
          }
      }
      

This does not compile because GetSomeValue() is not static and fields are intitialized before the objected is constructed. (A field initializer cannot reference the non-static field, method, or property).

A solution could be to initialize the property in the constructor:


      class Test
      {
          public Test()
          {
              _someValue = new Lazy<int>(GetSomeValue); 
          }
       
          private readonly Lazy<int> _someValue;
          public int SomeValue
          {
              get
              {
                  return _someValue.Value;
              }
          }
       
          private int GetSomeValue()
          {
              // Not static, calls other properties
              throw new NotImplementedException();
          }
      }
      

But this has some disadvantages:

  • You need to care about the sequence of code in the constructor. (Not calling something that is not already initialiazed)
  • When you view the code of a property, you can not see how the property is implemented.

Implement the property in the property, not in the constructor

The solution is create a lazy alternative that has a parameterless constructor:


      public class LazyValue<T>
      {
          private T _value;
          public bool IsInitialized { get; private set; }
          private readonly object _lock = new object();
       
          public T GetValue(Func<T> producer)
          {
              if (producer == null)
              {
                  throw new ArgumentNullException("producer");
              }
       
              if (!IsInitialized)
              {
                  lock (_lock)
                  {
                      if (!IsInitialized)
                      {
                          _value = producer();
                          IsInitialized = true;
                      }
                  }
              }
              return _value;
          }
       
          internal T Value
          {
              get
              {
                  if (!IsInitialized)
                  {
                      throw new FieldAccessException("This value needs to be set by using the GetValue() method or setting the Value property");
                  }
                  return _value;
              }
          }
      }
      

Now you can implement the property just like this:


      private readonly LazyValue<int> _someValue = new LazyValue<int>();
      public int SomeValue
      {
          get
          {
              return _someValue.GetValue(GetSomeValue);
          }
      }
       
      private int GetSomeValue()
      {
          // Not static, calls other properties
          throw new NotImplementedException();
      }
      

Or without a seperate Get-method like in this example:


      private readonly LazyValue<Guid> _id = new LazyValue<Guid>();
      public Guid Id
      {
          get
          {
              return _id.GetValue(Guid.NewGuid);
          }
      }
      

Lazy with IEnumerable, a separate lazy class

There is an other problem with Lazy. In the next class Lazy is useless:


      class Test
      {
          private readonly Lazy<IEnumerable<Guid>> _someValues =
              new Lazy<IEnumerable<Guid>>(GetSomeValues);
       
          public IEnumerable<Guid> SomeValues
          {
              get
              {
                  return _someValues.Value;
              }
          }
       
          private static IEnumerable<Guid> GetSomeValues()
          {
              for (int i = 0; i < 40; i++)
              {
                  yield return Guid.NewGuid();
              }
          }
      }
      

When running this code...


      var t = new Test();
      foreach (var value in t.SomeValues.Take(3))
      {
          Console.WriteLine(value);
      }
      foreach (var value in t.SomeValues.Take(3))
      {
          Console.WriteLine(value);
      }
      

... it returns 6 different Guid's! (And not: guid 4, 5 and 6 are the same as 1, 2 and 3). The "solution" with Lazy is to add ".ToList()" to the constructor in the field:


      private readonly Lazy<IEnumerable<Guid>> _someValues = 
          new Lazy<IEnumerable<Guid>>(() => GetSomeValues().ToList());
      

That is not really lazy because it creates 40 Guid's to show only 3 Guid's!

The solution is to use this class:


      public class LazyEnumerableValue<T>
      {
          // LazyList<T>, see other post:
          // https://www.siepman.nl/Home/Blog/363e34a3-f2a0-4be9-ada9-a6a2e6b07672
          private readonly LazyValue<LazyList<T>> _cache = new LazyValue<LazyList<T>>();
       
          public IEnumerable<T> GetValue(Func<IEnumerable<T>> producer)
          {
              return _cache.GetValue(() => CacheEnumerable(producer));
          }
       
          private LazyList<T> CacheEnumerable(Func<IEnumerable<T>> producer)
          {
              var value = producer();
              var result = value.ToLazyList();
              return result;
          }
       
          public bool AllElementsAreCached
          {
              get
              {
                  return _cache.IsInitialized && _cache.Value.AllElementsAreCached;
              }
          }
      }
      

Now it works as simple as this:


      private readonly LazyEnumerableValue<Guid> _someValues =
          new LazyEnumerableValue<Guid>();
       
      public IEnumerable<Guid> SomeValues
      {
          get
          {
              return _someValues.GetValue(GetSomeValues);
          }
      }
       
      private static IEnumerable<Guid> GetSomeValues()
      {
          for (int i = 0; i < 40; i++)
          {
              yield return Guid.NewGuid();
          }
      }
      

Combine the 2 classes, the ultimate caching class

To make it really easy for the user of the two classes LazyValue and LazyEnumerableValue, I have combined those two classes. If the value implements IEnumerable but not IList, it automatically converts the IEnumerable to a LazyList with a 'dynamic trick' so it is only enumerated once and not more than necessary. The disadvantage is that the first call of GetValue() is slightly slower.


      public class LazyValue<T>
      {
          private T _value;
          public bool IsInitialized { get; private set; }
          private readonly object _lock = new object();
       
          public T GetValue(Func<T> producer)
          {
              if (producer == null)
              {
                  throw new ArgumentNullException("producer");
              }
       
              if (!IsInitialized) //  Double-checked locking pattern
              {
                  lock (_lock) 
                  {
                      if (!IsInitialized) //  Double-checked locking pattern
                      {
                          _value = ConvertToListIfNecessary(producer());
       
                          IsInitialized = true;
                      }
                  }
              }
              return _value;
          }
       
          private T ConvertToListIfNecessary(dynamic value)
          {
              return MaybeToList(value);
          }
       
          private LazyList<TP> MaybeToList<TP>(IEnumerable<TP> value)
          {
              // LazyList<T>, see other post:
              // https://www.siepman.nl/Home/Blog/363e34a3-f2a0-4be9-ada9-a6a2e6b07672
              return new LazyList<TP>(value);
          }
       
          private IList<TP> MaybeToList<TP>(IList<TP> value)
          {
              return value;
          }
       
          private object MaybeToList(object value)
          {
              return value;
          }
      }
      

Credits:

I used input from two people for this post:

  • Frank Bakker for the initial "GetValue(Func<> producer)" solution. (No longer a func in the constructor like Laz<>).
  • Jon Skeet for the "Dynamic trick"

Leave a Comment

Comment

Comments