List<T>.ForEach
is looking for an Action<T>
. When you write
n.ForEach(Console.WriteLine);
what you have here is one of the members of the method group Console.WriteLine
playing the role of an Action<T>
. The compiler will look for the best overload of Console.WriteLine
that eats instances of int
. In fact, it will use the overload Console.WriteLine(int)
. It will then use this overload to play the role of an Action<int>
.
For details on how this is done, see §6.6 of the specification (Method group conversions).
However, when you write
n.ForEach(i => Console.WriteLine(i));
we actually have a very different Action<int>
In the first case, the Action<int>
was Console.WriteLine(int)
. Here, the Action<int>
is equivalent to you having written
public static void DoSomething(int i) {
Console.WriteLine(i);
}
and then
n.ForEach(DoSomething);
(Of course, the compiler has to go through the same method group process as described above to figure out what is meant by DoSomething
).
The point is that in the first case the Action<int>
is Console.WriteLine(int)
. However, in the second case the Action<int>
is a middle man (the lambda expression) that itself will call Console.WriteLine(int)
.