如果可用,使用C#方法组有什么好处吗?

时间:2010-10-01 18:20:11

标签: c# method-group

处理List<string>之类的内容时,您可以写下以下内容:

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

或者您可以使用方法组执行相同的操作:

list.ForEach(Console.WriteLine);

我更喜欢第二行代码,因为它对我来说看起来更干净,但这对它有什么好处吗?

7 个答案:

答案 0 :(得分:23)

好吧,让我们来看看会发生什么。

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>委托,lambda表达式方法创建隐藏的匿名委托字段,并在必要时对其进行内联初始化。请注意brtrue处的IL_000a指示。

答案 1 :(得分:8)

使用lambda表达式时有一个额外的间接级别。使用像这样的非闭包表达式,你只需要在其间进行额外的方法调用,如其他人所提到的那样。

但是有一些有趣的差异。在第二种情况下,每次调用都会创建一个新的委托实例。对于前者,委托创建一次并缓存为隐藏字段,因此如果您要调用很多,则可以节省分配。

此外,如果将局部变量引入lambda表达式,它将成为闭包,而不是仅生成本地方法,将创建一个新类来保存此信息,这意味着在那里进行额外分配。

答案 2 :(得分:8)

正如其他人所指出的那样,由lambda引起的额外不必要的间接层。但是,语言也存在细微差别。例如,在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

答案 3 :(得分:7)

我相信有一个好处。在第一种情况下,您将创建一个调用Console.Writeline(string)函数的匿名方法,而在另一种情况下,您只是将引用传递给现有函数。

答案 4 :(得分:3)

是;第一个实际上可以导致不必要的额外临时呼叫发生;将x传递给简单调用Console.WriteLine(x);的方法您不需要执行第一个,因为Console.WriteLine已经是一个与ForEach正在寻找的签名匹配的方法。

答案 5 :(得分:0)

就我个人而言,我也更喜欢第二种,因为调试时不那么容易混淆,但在这种情况下,我认为这只是风格问题,因为它们最终都会完成同样的事情。

答案 6 :(得分:0)

除了让那些喜欢方法组的人更加愉快之外没有任何实际好处,并且让那些不喜欢他们的人烦恼[应该让你高兴。]此外,它会使你的代码与早期的编译器不兼容。

-Oisin