Подтвердить что ты не робот

Странность группы методов С#

Я обнаружил что-то очень странное, что я надеюсь лучше понять.

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));

Как можно оставить параметр выражения лямбда (i = > ) и по-прежнему иметь текущий элемент, переданный в console.WriteLine?

Спасибо за понимание. -Keith

4b9b3361

Ответ 1

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).

Ответ 2

Это менее запутанно, если вы считаете, что на самом деле происходит.

Вы передаете метод аргументу делегата. В большинстве случаев мы думаем о делегатах в контексте событий, но они также могут быть параметрами для методов. Это не кажется странным, когда метод добавляется к событию без аргументов, это просто необычно выглядит, когда выполняется в этом контексте.

До лямбда, вы должны были делать это все время, и было так больно, что никогда не подумайте об использовании библиотеки, похожей на LINQ. С Lambdas это легче сделать, но вы всегда можете делать и старый способ.