2010-02-09 45 views
13

我发现了一些很奇怪的东西,希望能够更好的理解。C#方法组陌生性

var all = new List<int[]>{ 
       new int[]{1,2,3}, 
       new int[]{4,5,6}, 
       new int[]{7,8,9} 
       }; 

all.ForEach(n => n.ForEach(i => Console.WriteLine(i))); 

其可以被重写为:

... 
all.ForEach(n => n.ForEach(Console.WriteLine)); 

如何能够离开了lambda表达式参数(I =>),并且仍然在当前项传递给console.WriteLine?

感谢您的任何见解。 -Keith

回答

33

正在查找Action<T>。当你写

n.ForEach(Console.WriteLine); 

你有什么这里是发挥Action<T>的作用的方法组Console.WriteLine的成员之一。编译器会寻找Console.WriteLine的最佳过载,它会吃掉int的实例。实际上,它会使用过载Console.WriteLine(int)。然后它将使用这个超载来扮演Action<int>的角色。

有关如何完成的详细信息,请参阅规范的第6.6节(方法组转换)。

但是,当你写

n.ForEach(i => Console.WriteLine(i)); 

我们实际上有一个非常不同的Action<int>在第一种情况下,Action<int>Console.WriteLine(int)。在这里,Action<int>相当于你写过

public static void DoSomething(int i) { 
    Console.WriteLine(i); 
} 

然后

n.ForEach(DoSomething); 

(当然,编译器必须通过相同的方法组如上所述方法弄清楚是什么意思通过DoSomething)。

问题是,在第一种情况下,Action<int>Console.WriteLine(int)。然而,在第二种情况下,Action<int>是一个中间人(lambda表达式),它本身将调用Console.WriteLine(int)

+1

++为编译器的图像“吃”了Console.WriteLine的重载! – 2010-02-09 16:10:22

+1

非常好地阐述。谢谢! – Keith 2010-02-09 16:45:18

+0

很好的解释。 – 2010-10-28 09:36:04

2

如果你考虑到真正发生的事情,这个问题就不那么令人困惑了。

您正在将一个方法传递给委托参数。大多数情况下,我们会考虑事件背景下的代表,但它们也可能是方法的参数。当一个方法被添加到一个没有参数的事件中时,看起来并不奇怪,在这种情况下执行时看起来很不寻常。

在lambdas之前,你必须一直这样做,而且这是一个非常痛苦的事情,人们永远不会考虑使用类似于LINQ的库。有了兰姆达斯,这样做更容易,但你也可以一直做旧。

+0

C#2。0有匿名委托语法('delegate(){}'),但它仍然不够甜,语法糖会让LINQ看起来很杂乱。 – Gabe 2011-01-30 05:52:53