Есть ли какие-либо преимущества использования группы методов C #, если она доступна? - PullRequest
19 голосов
/ 01 октября 2010

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

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

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

list.ForEach(Console.WriteLine);

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

Ответы [ 7 ]

23 голосов
/ 01 октября 2010

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

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.

8 голосов
/ 02 октября 2010

Как уже отмечали другие, есть еще один ненужный слой косвенности, вызванный лямбдой. Однако есть и тонкие языковые различия. Например, в C # 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

8 голосов
/ 01 октября 2010

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

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

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

7 голосов
/ 01 октября 2010

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

3 голосов
/ 01 октября 2010

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

0 голосов
/ 01 октября 2010

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

-Oisin

0 голосов
/ 01 октября 2010

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

...