C#方法组怪异

我发现了一些非常奇怪的东西,我希望能更好地理解。

var all = new List{ 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

List.ForEach正在寻找一个Action 。 当你写作

 n.ForEach(Console.WriteLine); 

你在这里有一个方法组Console.WriteLine的成员之一扮演Action的角色。 编译器将查找吃掉int实例的Console.WriteLine的最佳重载。 实际上,它将使用重载Console.WriteLine(int) 。 然后它将使用此重载来扮演Action的角色。

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

但是,当你写作

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

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

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

然后

 n.ForEach(DoSomething); 

(当然,编译器必须通过与上述相同的方法组过程来弄清楚DoSomething含义)。

关键是在第一种情况下, Action Console.WriteLine(int) 。 但是,在第二种情况下, Action是一个中间人(lambda表达式),它本身将调用Console.WriteLine(int)

如果你考虑到真正发生的事情,这就不那么容易混淆了。

您正在将方法传递给委托参数。 大多数情况下,我们在事件的上下文中考虑委托,但它们也可以作为方法的参数。 将方法添加到没有参数的事件时似乎并不奇怪,在此上下文中执行时只是exception。

在lambdas之前,你必须一直这样做,这是一种痛苦,人们永远不会考虑使用看起来像LINQ的库。 使用Lambdas,这样做更容易,但您也可以使用旧方法。