List<T>.ForEach
ищет Action<T>
. Когда ты пишешь
n.ForEach(Console.WriteLine);
у вас есть один из членов группы методов Console.WriteLine
, играющий роль Action<T>
. Компилятор будет искать лучшую перегрузку 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>
- это средний человек (лямбда-выражение), который сам по себе назовет Console.WriteLine(int)
.