延迟加载主要应用在以下场景:


数据层 – ADO.NET或Entity Framework等ORM
反射 – 加载assemblies, types, MEF
缓存对象,领域实体


下面以领域实体为例, 在实体中我们经常检查某个属性下字段是否为空引用. 如果是空的话,计算或填充这个字段. 像这样:


1:      /// <summary>
2:      /// Order
3:      /// </summary>
4:      public class Order
5:      {
6:          private Customer _customer;
7:          /// <summary>
8:          /// Gets the customer.
9:          /// </summary>
10:          public Customer Customer
11:          {
12:              get
13:              {
14:                  if (_customer == null)
15:                  {
16:                      _customer = new Customer();
17:                  } return _customer;
18:              }
19:          }
20:          /// <summary>
21:          /// Prints the label.
22:          /// </summary>
23:          /// <returns></returns>
24:          public string PrintLabel()
25:          {
26:              return Customer.CompanyName + "\n" + Customer.Address;
27:          }
28:      }


如果PrintLabel方法写成这样:


1:          public string PrintLabel()
2:          {
3:              string result = _customer.CompanyName; // probably results in a NullReferenceException
4:              return result + "\n" + Customer.Address; // ok to access Customer
5:          }


注意上面第4行代码,可能有空引用异常. 下面我们使用​​Lazy<T>​​来解决这个问题:


1:      /// <summary>
2:      /// Order
3:      /// </summary>
4:      public class Order
5:      {
6:          /// <summary>
7:          /// _customerInitializer
8:          /// </summary>
9:          private Lazy<Customer> _customerInitializer;
10:
11:          /// <summary>
12:          /// Initializes a new instance of the <see cref="Order"/> class.
13:          /// </summary>
14:          public Order()
15:          {
16:              _customerInitializer = new Lazy<Customer>(() => new Customer(),isThreadSafe:true);
17:          }
18:
19:          /// <summary>
20:          /// Gets the customer.
21:          /// </summary>
22:          public Customer Customer
23:          {
24:              get{ return _customerInitializer.Value;}
25:          }
26:
27:          /// <summary>
28:          /// Prints the label.
29:          /// </summary>
30:          /// <returns></returns>
31:          public string PrintLabel()
32:          {
33:              string result = Customer.CompanyName; // ok to access Customer
34:              return result + "\n" + _customerInitializer.Value.Address; // ok to access via .Value
35:          }
36:      }


上面的代码16行,我们延迟初始化对像实例,并且设置线程安全. Lazy<T>内部是使用Func<T>来实现的,看下面其中构造器的源代码:


1:  public Lazy(Func<T> valueFactory, LazyThreadSafetyMode mode)
2:  {
3:      if (valueFactory == null) throw new ArgumentNullException("valueFactory");
4:      this.m_threadSafeObj = Lazy<T>.GetObjectFromMode(mode);
5:      this.m_valueFactory = valueFactory;
6:  }


有兴趣请自行查看.net framework中的源码. 我们还可以扩展一个LazyNotNull<T>


1:      /// <summary>
2:      /// LazyNotNull
3:      /// </summary>
4:      /// <typeparam name="T">Type</typeparam>
5:      public class LazyNotNull<T>
6:      {
7:          private Lazy<T> _lazyValue = null;
8:          private Func<T> _valueFactory = null;
9:
10:          /// <summary>
11:          /// Initializes a new instance of the <see cref="LazyNotNull<T>"/> class.
12:          /// </summary>
13:          /// <param name="valueFactory">The value factory.</param>
14:          public LazyNotNull(Func<T> valueFactory)
15:          {
16:              _lazyValue = new Lazy<T>(valueFactory);
17:              _valueFactory = valueFactory;
18:          }
19:
20:          /// <summary>
21:          /// Gets T value.
22:          /// </summary>
23:          public T Value
24:          {
25:              get
26:              {
27:                  var lazyValue = _lazyValue;
28:                  if (lazyValue.Value != null)
29:                  {
30:                      return lazyValue.Value;
31:                  }
32:                  _lazyValue = new Lazy<T>(_valueFactory);
33:                  return default(T);
34:              }
35:          }
36:      }


希望这篇POST对您开发有帮助.


作者:Petter Liu

本文版权归作者,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

该文章也同时发布在我的独立博客中-Petter Liu Blog。