標籤:f# 函數編程 實用函數編程 延遲計算 c#
11.3.5 為 C# 實現延遲值
在 11.3.3 節,我們使用函數來表示 C# 中的延遲計算。我們剛才在 F# 中探討了Lazy<T> 類型,它為計算過的值,添加了緩衝功能。從Visual Studio 2010 開始,在核心的 .NET 庫下的System.Lazy <T> 就有了這種類型,因此,我們不必自己實現。
清單 11.18 是簡化的 Lazy<T> 類。代碼在許多方面做了簡化,它不是安全執行緒的,不處理任何異常,只表達了核心概念。
[
清單的序號終於正常了。
]
清單11.18 表示延遲值的類 (C#)
public class Lazy<T> {
readonly Func<T> func;
bool evaluated = false; | 表示緩衝的狀態
Tvalue; |
public Lazy(Func<T> func) { [1]
this.func = func;
}
public T Value { [2]
get {
if (!evaluated) {
value = func(); | 計算值,修改狀態
evaluated = true; |
}
return value;
}
}
}
public class Lazy { <-- 在建立值時,啟用類型推斷
public static Lazy<T> Create<T>(Func<T> func) {
return new Lazy<T>(func);
}
}
這個類的第一個重要部分是建構函式[1],它的參數為函數,並將其儲存在唯讀欄位裡面;而這個函數沒有任何參數,只在調用時,才計算這個值,所以,我們使用 Func<T> 委託。在非泛型型別中,還有一個靜態方法,讓我們建立延遲值時,更容易使用 C# 的類型推斷。
延遲值用一個標誌來表示值是否已經計算過。注意,我們將使用泛型,因此,使用 null 值來表示不可能很容易,雖然,我們添加了約束,來強制 T 是參考型別,我們需要允許這種可能性,函數可能返回 null 作為計算值。
使用緩衝值的大部分代碼是在 Value 屬性[2]的 getter 中。從使用者的角度來看,這是類的第二個重要部分。它首先測試是否已進行計算過函數。如果已經做過,我們就可以使用前面計算過的值;如果還沒有,就調用函數,並作標誌,保證不會多次計算。
現在,我們看一段簡單代碼,看看是如何使用這個類型的:
var lazy = Lazy.Create(() => Foo(10));
Console.WriteLine(lazy.Value); // Prints ‘Foo(10)‘ and ‘True‘
Console.WriteLine(lazy.Value); // Prints only ‘True‘
如果嘗試此代碼,可以看到,其行為與 F# 版本完全相同。當建立延遲值時,我們給它一個函數:這時,Foo 方法不會調用。第一次調用Value,計算函數並調用 Foo;後序再調用 Value ,就使用前面計算過的緩衝值,可以看最後一行的列印結果。
到目前為止,我們討論延遲值的動機是實現或運算子的延遲版本的痛點。在下一節,我們將看兩個更複雜的實際使用延遲值的樣本。
11.3.5 為 C# 實現延遲值