Are there any benefits to using a C# method group if available?

社会主义新天地 提交于 2019-11-27 14:20:46

Well, lets take a look and see what happens.

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

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

This gets compiled into the following 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 
}

Notice how the method group approach creates an Action<T> delegate for one time use and the lambda expression approach creates a hidden anonymous delegate field and does an inline initialization of it if necessary. Notice brtrue instruction at IL_000a.

There is an extra level of indirection when using the lambda expression. With a non-closure expression like that, you'll simply have an extra method call in-between, as mentioned by others.

There are a few interesting differences though. In the second case, a new delegate instance is being created on each call. For the former, the delegate is created once and cached as a hidden field, so if you're calling a lot you'll save on allocations.

Additionally, if you introduce a local variable into the lambda expression, it becomes a closure and instead of just a local method being generated, a new class will be created to hold this information, meaning an extra allocation there.

As others have noted, there is an extra unnecessary layer of indirection induced by the lambda. However, there are subtle language differences as well. For example, in C# 3 generic type inference works differently on M(F) than on M(x=>F(x)) when attempting to perform return type inference.

For details see:

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

and the follow-up:

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

I believe that there is a benefit. In first case you are creating anonymous method which calls Console.Writeline(string) function while in the other case you are just passing the reference to existing function.

Yes; the first actually can cause an unnecessary extra, interim call to happen; passing x in to a method that simply calls Console.WriteLine(x); You don't need to do the first one because Console.WriteLine already is a method which matches the signature that ForEach is looking for.

Personally I also prefer the second because it's less confusing to debug, but in this case I think it's just a matter of style since they both end up getting the same thing done.

No tangible benefits other than making it more pleasant to people who like method groups, and annoy people who dislike them [should that please you.] Also, it makes your code incompatible with earlier compilers.

-Oisin

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!