views:

232

answers:

7

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?

+3  A: 

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.

Andrew Barber
A: 

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.

Reinderien
A: 

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

x0n
What compilers accept the second and not the first?
Jon Hanna
+3  A: 

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.

Ivan Ferić
Yup, that was my feeling, too. I imagine it's possible the optimizer may recognize this and strip out the extra, unneeded call, but when it's actually easier to write it the 'better' way, it makes sense to do so, IMO. I wrote a blog post about this topic (unnecessarily using Lambda Expressions like this: http://www.andrewbarber.com/post/When-to-Avoid-Lambda-Expressions-or-What-Anonymous-Methods.aspx )
Andrew Barber
+2  A: 

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.

MikeP
+6  A: 

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.

Brian Gideon
how can I view the compiled IL ?
M.H
@M.H.: Use ILDASM or Reflector.
Brian Gideon
+4  A: 

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

Eric Lippert