对象的创建方式,始终代表了软件工业的生产力方向,代表了先进软件技术发展的方向,也代表了广大程序开发者的集体智慧。以new的方式创建,通过工厂方法,利用IoC容器,都以不同的方式实现了活生生实例成员的创生。而本文所关注的Lazy<T>也是干这事儿的。不过,简单说来,Lazy<T>要实现的就是按“需”创建,而不是按时创建。
我们往往有这样的情景,一个关联对象的创建需要较大的开销,为了避免在每次运行时创建这种家伙,有一种聪明的办法叫做实现“懒对象”,或者延迟加载。.NET 4.0之前,实现懒对象的机制,需要开发者自己来实现与管理,例如,你可以翻开老赵同志的较为理想的延迟代理的编写方式一文来了解其原理和场合。可喜的是,在.NET 4.0中包含的另一个好玩的家伙System.Lazy<T>。它的定义如下:
[Serializable] public class Lazy<T> { public Lazy(); public Lazy(bool isThreadSafe); public Lazy(Func<T> valueFactory); public Lazy(Func<T> valueFactory, bool isThreadSafe); public bool IsValueCreated { get; } public T Value { get; } public override string ToString(); }
注:VS2010 Beta2对Lazy<T>和VS2010 Beta1有较大差异,因此本文仅以最新版本为标准,并不保证最终.NET 4.0正式版的实际情况。
假设,我们有一个大块头:
public class Big { public int ID { get; set; } // Other resources }
那么,可以使用如下的方式来实现Big的延迟创建:
static void Main(string[] args) { Lazy<Big> lazyBig = new Lazy<Big>(); }
从Lazy<T>的定义可知,其Value属性就是我们包装在Lazy Wrapper中的真实Big对象,那么当我们第一次访问lazyBig.Value时,就回自动的创建Big实例。
static void Main(string[] args) { Lazy<Big> lazyBig = new Lazy<Big>(); Console.WriteLine(lazyBig.Value.ID); }
当然,有其定义可知,Lazy远没有这么小儿科,它同时还可以为我们提供以下的服务:
- 通过IsValueCreated,获取是否“已经”创建了实例对象。
- 解决非默认构造函数问题。
显而易见。我们的Big类并没有提供带参数构造函数,那么如下的Big类:
public class Big { public Big(int id) { this.ID = id; } public int ID { get; set; } // Other resources }
上述创建方式将引发运行时异常,提示包装对象没有无参的构造函数。那么,这种情形下的延迟加载,该如何应对呢?其实Lazy<T>的构造中还包括:
public Lazy(Func<T> valueFactory);
它正是用来应对这样的挑战:
static void Main(string[] args) { // Lazy<Big> lazyBig = new Lazy<Big>(); Lazy<Big> lazyBig = new Lazy<Big>(() => new Big(100)); Console.WriteLine(lazyBig.Value.ID); }
其实,从public Lazy(Func<T> valueFactory)的定义可知,valueFactory可以返回任意的T实例,那么任何复杂的构造函数,对象工厂或者IoC容器方式都可以在此以轻松的方式兼容,例如:
public class BigFactory { public static Big Build() { return new Big(100); } }
可以应用Lazy<T>和BigFactory实现Big的延迟加载:
static void Main(string[] args) { Lazy<Big> lazyBig = new Lazy<Big>(() => BigFactory.Build()); Console.WriteLine(lazyBig.Value.ID); }
- 提供多线程环境支持。
另外的构造器:
public Lazy(bool isThreadSafe); public Lazy(Func<T> valueFactory, bool isThreadSafe);
中,isThreadSafe则应用于多线程环境下,如果isThreadSafe为false,那么延迟加载对象则一次只能创建于一个线程。
关于Lazy<T>的应用,其实已经不是一个纯粹的语言问题,还涉及了对设计的考量,例如实现整个对象的延迟加载,或者实现延迟属性,考量线程安全等等。既然是点滴,就不说教太多。因为,.NET 4.0提供的关注度实在不少,我们眼花缭乱了。
参考文献
- Lazy Initialization, http://msdn.microsoft.com/en-us/library/dd997286(VS.100).aspx