物件的建立方式始終反映了軟體工業的生產力方向,也代表了先進軟體技術演進的軌跡。從直接使用 new、工廠模式,到 IoC 容器,各種模式都在解決實例建立的問題。而 .NET 4.0 引入的 System.Lazy<T>,則專注於「按需建立」而非「按時建立」,為延遲初始化提供了標準化、執行緒安全的解決方案。
在實際開發中,常會遇到某些關聯物件建立成本極高(如大量記憶體配置、資料庫連線、複雜運算)的情況。若在程式啟動時就建立這些物件,會嚴重拖慢啟動速度並浪費資源。傳統做法需開發者自行實作「懶惰物件」模式(參考老趙的《延遲代理的編寫方式》),但 .NET 4.0 內建的 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 版本,最終正式版可能有細微差異。
假設有一個資源佔用大的 Big 類別:
public class Big
{
public int ID { get; set; }
// 其他昂貴資源...
}
使用 Lazy<Big> 包裝,實例只在首次存取 Value 屬性時才會建立:
static void Main(string[] args)
{
Lazy<Big> lazyBig = new Lazy<Big>();
// 此時 Big 尚未建立
Console.WriteLine(lazyBig.Value.ID); // 首次存取,觸發建立
}
若 Big 沒有無參數建構函式,例如:
public class Big
{
public Big(int id) { this.ID = id; }
public int ID { get; set; }
}
直接使用 new Lazy<Big>() 會拋出異常。此時應使用接受 Func<T> 的建構函式:
static void Main(string[] args)
{
// 使用 Lambda 表達式指定建立邏輯
Lazy<Big> lazyBig = new Lazy<Big>(() => new Big(100));
Console.WriteLine(lazyBig.Value.ID); // 輸出 100
}
這個工廠委派可以整合任何複雜的建立邏輯,包括物件工廠、IoC 容器解析等:
public class BigFactory
{
public static Big Build() => new Big(100);
}
// 使用工廠方法
Lazy<Big> lazyBig = new Lazy<Big>(() => BigFactory.Build());
Lazy<T> 提供了兩個帶 isThreadSafe 參數的建構函式:
public Lazy(bool isThreadSafe)public Lazy(Func<T> valueFactory, bool isThreadSafe)當 isThreadSafe = true(預設值)時,確保多執行緒環境下物件只會被建立一次;設為 false 則移除鎖定機制,效能略高但需開發者自行保證單執行緒存取。
透過 IsValueCreated 唯讀屬性,可隨時檢查實例是否已經被建立,避免不必要的觸發:
if (!lazyBig.IsValueCreated)
{
// 執行某些預備工作...
}
var instance = lazyBig.Value;
Lazy<T> 的應用不僅是語法層面,更涉及架構設計:
善用 Lazy<T> 能讓資源管理更具彈性,是 .NET 4.0 中不可或缺的工具類別。