[英]C# lambda expressions and lazy evaluation
lambda 表達式的一個優點是您必須僅在需要其結果時才對函數求值。
在以下(簡單)示例中,僅當作者存在時才評估 text 函數:
public static void PrintLine(Func<string> text, TextWriter writer)
{
if (writer != null)
{
writer.WriteLine(text());
}
}
不幸的是,這使得使用代碼有點難看。 你不能用常量或變量來調用它
PrintLine("Some text", Console.Out);
並且必須這樣稱呼它:
PrintLine(() => "Some text", Console.Out);
編譯器無法從傳遞的常量“推斷”無參數函數。 是否有計划在未來版本的 C# 中改進這一點,或者我遺漏了什么?
更新:
我自己剛剛發現了一個骯臟的黑客:
public class F<T>
{
private readonly T value;
private readonly Func<T> func;
public F(T value) { this.value = value; }
public F(Func<T> func) {this.func = func; }
public static implicit operator F<T>(T value)
{
return new F<T>(value);
}
public static implicit operator F<T>(Func<T> func)
{
return new F<T>(func);
}
public T Eval()
{
return this.func != null ? this.func() : this.value;
}
}
現在我可以將函數定義為:
public static void PrintLine(F<string> text, TextWriter writer)
{
if (writer != null)
{
writer.WriteLine(text.Eval());
}
}
並用函數或值調用它。
你可以使用過載: -
public static void PrintLine(string text, TextWriter writer)
{
PrintLine(() => text, writer);
}
編譯器非常擅長推斷類型 ,它不擅長推斷意圖 。 關於C#3中所有新語法糖的一個棘手問題是,它們可能導致混淆編譯器對它們的作用。
考慮你的例子:
() => "SomeText"
編譯器會看到這一點並理解您打算創建一個不帶參數的匿名函數並返回一種System.String。 這是從你給它的lambda表達式推斷出來的。 實際上你的lambda被編譯為:
delegate {
return "SomeText";
};
它是您要發送到PrintLine
執行的匿名函數的委托。
它在過去一直很重要,但現在使用LINQ,lambdas,迭代器塊,自動實現的屬性,除了其他功能之外,使用像.NET Reflector這樣的工具在編譯后查看代碼是至關重要的。看看究竟是什么讓這些功能發揮作用。
不幸的是,丑陋的語法就是你在C#中所擁有的。
來自更新的“臟黑客”不起作用,因為它不會延遲字符串參數的評估:它們在傳遞給operator F<T>(T value)
之前得到評估。
將PrintLine(() => string.Join(", ", names), myWriter)
與PrintLine(string.Join(", ", names), myWriter)
在第一種情況下,字符串僅在打印時才連接; 在第二種情況下,無論如何都將字符串連接起來:只有打印是有條件的。 換句話說,評估並不是懶惰的。
那兩個陳述完全不同。 一個是定義一個函數,另一個是聲明。 混淆語法會更棘手。
() => "SomeText" //this is a function
"SomeText" //this is a string
你可以在String上編寫一個擴展方法來粘貼它。你應該可以編寫“Some text”.PrintLine(Console.Out); 並讓它為你工作。
奇怪的是,幾個星期前我做了一些懶惰的lambda表達式評估,並在這里寫博客 。
說實話,我並不完全理解你的問題,但你的解決方案對我來說似乎有點復雜。
我認為我使用 lambda 調用解決的問題類似,也許您可以將其用作靈感:我想查看字典中是否存在鍵,如果不存在,則需要執行(昂貴的)加載操作。
public static class DictionaryHelper
{
public static TValue GetValueOrLambdaDefault<TKey, TValue> (this IDictionary<TKey, TValue> dictionary, TKey key, Func<TValue> func)
{
if (dictionary.ContainsKey(key))
return dictionary[key];
else
return func.Invoke();
}
}
[TestClass]
public class DictionaryHelperTest
{
[TestMethod]
public void GetValueOrLambdaDefaultTest()
{
var dict = new Dictionary<int, string>();
try
{
var res1 = dict.GetValueOrLambdaDefault(1, () => LoadObject());
Assert.Fail("Exception should be thrown");
}
catch { /*Exception should be thrown*/ }
dict.Add(1, "");
try
{
var res1 = dict.GetValueOrLambdaDefault(1, () => LoadObject());
}
catch { Assert.Fail("Exception should not be thrown"); }
}
public static string LoadObject()
{
throw new Exception();
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.