Reputation: 6133
When dealing with something like a List<string>
you can write the following:
list.ForEach(x => Console.WriteLine(x));
or you can use a method group to do the same operation:
list.ForEach(Console.WriteLine);
I prefer the second line of code because it looks cleaner to me, but are there any benefits to this?
Upvotes: 24
Views: 3990
Reputation: 48959
Well, let's 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
.
Upvotes: 29
Reputation: 660289
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:
and the follow-up:
https://learn.microsoft.com/en-us/archive/blogs/ericlippert/method-type-inference-changes-part-zero
Upvotes: 9
Reputation: 7959
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.
Upvotes: 13
Reputation: 4763
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.
Upvotes: 7
Reputation: 52450
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
Upvotes: 0
Reputation: 15253
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.
Upvotes: 0
Reputation: 40150
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.
Upvotes: 3