2009-01-05 146 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)在第一种情况下,字符串仅在打印时才连接;在第二种情况下,字符串无论如何连接:只有打印是有条件的。换句话说,评估不是懒惰的。