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

Существуют ли какие-либо преимущества при использовании группы методов С#, если они доступны?

При работе с чем-то вроде List<string> вы можете написать следующее:

list.ForEach(x => Console.WriteLine(x));

или вы можете использовать группу методов для выполнения той же операции:

list.ForEach(Console.WriteLine);

Я предпочитаю вторую строку кода, потому что она выглядит более чистой для меня, но есть ли какие-либо преимущества для этого?

4b9b3361

Ответ 1

Хорошо, давайте взглянем и посмотрим, что произойдет.

static void MethodGroup()
{
    new List<string>().ForEach(Console.WriteLine);
}

static void LambdaExpression()
{
    new List<string>().ForEach(x => Console.WriteLine(x));
}

Это скомпилируется в следующий IL.

.method private hidebysig static void MethodGroup() cil managed
{
    .maxstack 8
    L_0000: newobj instance void [mscorlib]System.Collections.Generic.List`1<string>::.ctor()
    L_0005: ldnull 
    L_0006: ldftn void [mscorlib]System.Console::WriteLine(string)
    L_000c: newobj instance void [mscorlib]System.Action`1<string>::.ctor(object, native int)
    L_0011: call instance void [mscorlib]System.Collections.Generic.List`1<string>::ForEach(class [mscorlib]System.Action`1<!0>)
    L_0016: ret 
}

.method private hidebysig static void LambdaExpression() cil managed
{
    .maxstack 8
    L_0000: newobj instance void [mscorlib]System.Collections.Generic.List`1<string>::.ctor()
    L_0005: ldsfld class [mscorlib]System.Action`1<string> Sandbox.Program::CS$<>9__CachedAnonymousMethodDelegate1
    L_000a: brtrue.s L_001d
    L_000c: ldnull 
    L_000d: ldftn void Sandbox.Program::<LambdaExpression>b__0(string)
    L_0013: newobj instance void [mscorlib]System.Action`1<string>::.ctor(object, native int)
    L_0018: stsfld class [mscorlib]System.Action`1<string> Sandbox.Program::CS$<>9__CachedAnonymousMethodDelegate1
    L_001d: ldsfld class [mscorlib]System.Action`1<string> Sandbox.Program::CS$<>9__CachedAnonymousMethodDelegate1
    L_0022: call instance void [mscorlib]System.Collections.Generic.List`1<string>::ForEach(class [mscorlib]System.Action`1<!0>)
    L_0027: ret 
}

Обратите внимание, как подход группы методов создает делегат Action<T> для одноразового использования, а подход лямбда-выражения создает скрытое поле анонимного делегирования и выполняет встроенную инициализацию, если это необходимо. Обратите внимание на инструкцию brtrue на IL_000a.

Ответ 2

Как отмечали другие, существует лишний ненужный слой косвенности, вызванный лямбдой. Однако есть и тонкие языковые различия. Например, в С# 3 общий тип вывода работает по-разному на M(F), чем на M(x=>F(x)) при попытке выполнить вывод типа возвращаемого значения.

Подробнее см.:

http://blogs.msdn.com/b/ericlippert/archive/2007/11/05/c-3-0-return-type-inference-does-not-work-on-member-groups.aspx

и последующие действия:

http://blogs.msdn.com/b/ericlippert/archive/2008/05/28/method-type-inference-changes-part-zero.aspx

Ответ 3

Я считаю, что есть преимущество. В первом случае вы создаете анонимный метод, который вызывает функцию Console.Writeline(string), а в другом случае вы просто передаете ссылку на существующую функцию.

Ответ 4

При использовании выражения лямбда существует дополнительный уровень косвенности. С таким выражением не-закрытия, вы просто будете иметь дополнительный вызов метода между ними, как упоминалось другими.

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

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

Ответ 5

Да; первый фактически может вызвать ненужный дополнительный промежуточный вызов; передав x в метод, который просто вызывает Console.WriteLine(x); Вам не нужно делать первый, потому что Console.WriteLine уже является методом, который соответствует сигнатуре, которую ищет ForEach.

Ответ 6

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

Ответ 7

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

-Oisin