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

如果可以,使用C#方法组有什么好处吗?,c#,method-group,C#,Method Group,在处理类似于列表的内容时,您可以编写以下内容: list.ForEach(x => Console.WriteLine(x)); 也可以使用方法组执行相同的操作: list.ForEach(Console.WriteLine); 我更喜欢第二行代码,因为它看起来更干净,但这有什么好处吗?是的;第一种情况实际上会导致不必要的额外临时调用发生;将x传递给一个只调用Console.WriteLine(x)的方法您不需要执行第一个,因为Console.WriteLine已经是一个与ForEac

在处理类似于
列表的内容时,您可以编写以下内容:

list.ForEach(x => Console.WriteLine(x));
也可以使用方法组执行相同的操作:

list.ForEach(Console.WriteLine);

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

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

我个人也更喜欢第二个,因为它不太容易调试,但在这种情况下,我认为这只是一个风格问题,因为它们最终都完成了相同的事情。

除了让喜欢方法组的人更愉快,让不喜欢方法组的人感到恼火之外,没有其他切实的好处[如果你喜欢的话]。此外,这也使你的代码与早期的编译器不兼容


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

使用lambda表达式时,存在额外的间接级别。对于这样的非闭包表达式,您只需要在它们之间有一个额外的方法调用,正如其他人所提到的

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


另外,如果在LAMBDA表达式中引入局部变量,它将变成一个闭包,而不是生成一个本地方法,将创建一个新的类来保存这些信息,这意味着额外的分配。

,让我们看一下,看看会发生什么。

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

static void LambdaExpression()
{
    new List<string>().ForEach(x => Console.WriteLine(x));
}
静态void方法组()
{
新列表().ForEach(Console.WriteLine);
}
静态void LambdaExpression()
{
new List().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 
}
.method private隐藏静态void MethodGroup()cil托管
{
.maxstack 8
L_0000:newobj实例void[mscorlib]System.Collections.Generic.List`1::.ctor()
L_0005:ldnull
L_0006:ldftn void[mscorlib]System.Console::WriteLine(字符串)
L_000c:newobj实例void[mscorlib]System.Action`1::.ctor(对象,本机int)
L_0011:调用实例void[mscorlib]System.Collections.Generic.List`1::ForEach(类[mscorlib]System.Action`1)
L_0016:ret
}
.method private hidebysing static void LambdaExpression()cil managed
{
.maxstack 8
L_0000:newobj实例void[mscorlib]System.Collections.Generic.List`1::.ctor()
L_0005:ldsfld类[mscorlib]系统.操作'1沙盒.程序::CS$9_CachedAnonymousMethodDelegate1
L_000a:brtrue.s L_001d
L_000c:ldnull
L_000d:ldftn void Sandbox.Program::b__0(字符串)
L_0013:newobj实例无效[mscorlib]系统。操作'1::.ctor(对象,本机int)
L_0018:stsfld类[mscorlib]系统.操作'1沙盒.程序::CS$9_CachedAnonymousMethodDelegate1
L_001d:ldsfld类[mscorlib]系统.操作'1沙盒.程序::CS$9_ucachedanonymousmethoddelegate1
L_0022:调用实例void[mscorlib]System.Collections.Generic.List`1::ForEach(类[mscorlib]System.Action`1)
L_0027:ret
}

注意方法组方法如何创建一次使用的
操作
委托,lambda表达式方法如何创建一个隐藏的匿名委托字段,并在必要时对其进行内联初始化。请注意
brtrue
指令位于
IL_000a

正如其他人所指出的,lambda会导致额外的不必要的间接层。然而,也存在着微妙的语言差异。例如,在C#3中,当尝试执行返回类型推断时,泛型类型推断在
M(F)
上的工作方式不同于在
M(x=>F(x))
上的工作方式

详情请参阅:

以及后续行动:


好吧,ReSharper推荐第二个版本。所以它应该是正确的…“一个更聪明的人说这是正确的”并不能真正解释为什么。也许是对的。这可能是最好的答案。但这并不能回答“为什么”的问题。是的,这也是我的感觉。我想优化器可能会认识到这一点,并去掉多余的、不必要的调用,但是当用“更好”的方式编写时,这样做是有意义的,我想。我写了一篇关于这个主题的博客文章(不必要地使用像这样的Lambda表达式:)如何查看已编译的IL?@M.H:使用ILDASM或Reflector@M.H您可以使用LINQPad查看已编译的IL