2009-01-05 120 views
7

lambda表達式的一個優點是隻有在需要結果時才需要計算函數。C#lambda表達式和懶惰評估

在下面的(簡單)例如,文本功能僅被評估時,作者是本:

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#中改進這一點,或者我錯過了什麼?

UPDATE:

我只是找到了一個骯髒的黑客自己:

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()); 
    } 
} 

與函數或調用它都值。

回答

1

那麼這兩個陳述是完全不同的。一個是定義一個函數,另一個是一個聲明。混淆語法會更棘手。

() => "SomeText" //this is a function 

"SomeText" //this is a string 
+0

我同意。在添加過載的情況下,不可能區分[如果支持這種快捷方式]。 – Krisc 2010-04-27 18:22:33

3

你可以使用一個過載: -

public static void PrintLine(string text, TextWriter writer) 
{ 
    PrintLine(() => text, writer); 
} 
+1

這打破了目的。無論如何,您定義的PrintLine重載的參數都會被評估。在內部添加lambda僅用於不必要地加深調用堆棧。 – 2009-01-05 20:11:15

+0

不僅如此,您必須爲n個參數創建2^n個重載。 – Rauhotz 2009-01-05 23:37:22

1

你可以在字符串把它粘在編寫擴展方法你應該能夠編寫「一些文本」 .PrintLine(Console.Out。 );並讓它爲你做好工作。奇怪的是,我幾個星期前玩了一些懶惰的拉姆達表達式評估和blogged about it here

1

編譯器非常善於推測類型,它不擅長推測意圖。 C#3中所有新語法糖的一個棘手問題是,它們可能會導致編譯器對它們做什麼的混淆。

考慮您的例子:

() => "SomeText" 

編譯器看到這個,就知道要創建一個匿名函數,它不帶任何參數,並返回一個System.String類型。這是從你給它的lambda表達式推斷出來的。在現實中你的拉姆達被編譯到這一點:

delegate { 
    return "SomeText"; 
}; 

,這是一個代表到要發送到PrintLine執行此匿名函數。

過去一直很重要,但現在使用LINQ,lambdas,迭代器塊,自動實現的屬性,其中最重要的是使用像.NET Reflector這樣的工具來查看代碼之後的代碼編譯以查看真正使這些功能的工作原理。

3

我懷疑C#會得到這個功能,但D有。你所概述的是一種用C#實現惰性參數評估的合適方法,並且可能與D中的lazy非常相似,並且以更純的函數式語言進行編譯。

考慮到所有的事情,這四個額外的字符加上可選的空白空間並不是一個特別大的代價,因爲它正在成爲一個多範式的強類型語言,並且具有明顯的重載分辨率和表現力。

2

不幸的是,醜陋的語法是你在C#中所擁有的。

更新中的「髒破解」不起作用,因爲它不會延遲字符串參數的評估:它們在傳遞到operator F<T>(T value)之前會被評估。

比較PrintLine(() => string.Join(", ", names), myWriter)PrintLine(string.Join(", ", names), myWriter)在第一種情況下,字符串僅在打印時才連接;在第二種情況下,字符串無論如何連接:只有打印是有條件的。換句話說,評估不是懶惰的。