Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.
List.ForEach(Console.WriteLine);

List.ForEach(s => Console.WriteLine(s));

To me, the difference is purely cosmetic, but are there any subtle reasons why one might be preferred over the other?

share|improve this question

3 Answers 3

up vote 12 down vote accepted

Looking at the compiled code through ILSpy, there actually is a difference in the two references. For a simplistic program like this:

namespace ScratchLambda
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

    internal class Program
    {
        private static void Main(string[] args)
        {
            var list = Enumerable.Range(1, 10).ToList();
            ExplicitLambda(list);
            ImplicitLambda(list);
        }

        private static void ImplicitLambda(List<int> list)
        {
            list.ForEach(Console.WriteLine);
        }

        private static void ExplicitLambda(List<int> list)
        {
            list.ForEach(s => Console.WriteLine(s));
        }
    }
}

ILSpy decompiles it as:

using System;
using System.Collections.Generic;
using System.Linq;
namespace ScratchLambda
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            List<int> list = Enumerable.Range(1, 10).ToList<int>();
            Program.ExplicitLambda(list);
            Program.ImplicitLambda(list);
        }
        private static void ImplicitLambda(List<int> list)
        {
            list.ForEach(new Action<int>(Console.WriteLine));
        }
        private static void ExplicitLambda(List<int> list)
        {
            list.ForEach(delegate(int s)
            {
                Console.WriteLine(s);
            }
            );
        }
    }
}

If you look at the IL call stack for both, the Explicit implementation has a lot more calls (and creates a generated method):

.method private hidebysig static 
    void ExplicitLambda (
        class [mscorlib]System.Collections.Generic.List`1<int32> list
    ) cil managed 
{
    // Method begins at RVA 0x2093
    // Code size 36 (0x24)
    .maxstack 8

    IL_0000: ldarg.0
    IL_0001: ldsfld class [mscorlib]System.Action`1<int32> ScratchLambda.Program::'CS$<>9__CachedAnonymousMethodDelegate1'
    IL_0006: brtrue.s IL_0019

    IL_0008: ldnull
    IL_0009: ldftn void ScratchLambda.Program::'<ExplicitLambda>b__0'(int32)
    IL_000f: newobj instance void class [mscorlib]System.Action`1<int32>::.ctor(object, native int)
    IL_0014: stsfld class [mscorlib]System.Action`1<int32> ScratchLambda.Program::'CS$<>9__CachedAnonymousMethodDelegate1'

    IL_0019: ldsfld class [mscorlib]System.Action`1<int32> ScratchLambda.Program::'CS$<>9__CachedAnonymousMethodDelegate1'
    IL_001e: callvirt instance void class [mscorlib]System.Collections.Generic.List`1<int32>::ForEach(class [mscorlib]System.Action`1<!0>)
    IL_0023: ret
} // end of method Program::ExplicitLambda


.method private hidebysig static 
    void '<ExplicitLambda>b__0' (
        int32 s
    ) cil managed 
{
    .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (
        01 00 00 00
    )
    // Method begins at RVA 0x208b
    // Code size 7 (0x7)
    .maxstack 8

    IL_0000: ldarg.0
    IL_0001: call void [mscorlib]System.Console::WriteLine(int32)
    IL_0006: ret
} // end of method Program::'<ExplicitLambda>b__0'

while the Implicit implementation is more concise:

.method private hidebysig static 
    void ImplicitLambda (
        class [mscorlib]System.Collections.Generic.List`1<int32> list
    ) cil managed 
{
    // Method begins at RVA 0x2077
    // Code size 19 (0x13)
    .maxstack 8

    IL_0000: ldarg.0
    IL_0001: ldnull
    IL_0002: ldftn void [mscorlib]System.Console::WriteLine(int32)
    IL_0008: newobj instance void class [mscorlib]System.Action`1<int32>::.ctor(object, native int)
    IL_000d: callvirt instance void class [mscorlib]System.Collections.Generic.List`1<int32>::ForEach(class [mscorlib]System.Action`1<!0>)
    IL_0012: ret
} // end of method Program::ImplicitLambda
share|improve this answer
    
Note that this is the release build of the code from a quick scratch program, so there may be room for further optimization. But this is the default output from Visual Studio. –  Agent_9191 Dec 22 '11 at 14:28
    
+1 That's because the lambda syntax is actually wrapping the raw method call in an anonymous function <i>for no reason</i>. This is completely pointless, hence you should use the raw method group as the Func<> parameter when it's available. –  Ed Woodcock Dec 22 '11 at 14:39
    
Wow, you get the green tick, for the research! –  Benjol Dec 23 '11 at 7:01

I'd prefer the lambda syntax in general. When you see that, then it tells you what the type is. When you see Console.WriteLine, you'd have to ask the IDE what type it is. Of course, in this trivial example, it's obvious, but in the general case, it might not be so much.

share|improve this answer
    
I'd prefer the labmda syntax for consistency with the cases where it is required. –  bunglestink Dec 22 '11 at 12:25
4  
I'm not in a C# person, but in the languages I've used with lambdas (JavaScript, Scheme and Haskell) people would probably give you the opposite advice. I think that just shows how good style is language-dependent. –  Tikhon Jelvis Dec 22 '11 at 12:43

with the two examples you gave they differ in that when you say

List.ForEach(Console.WriteLine) 

you are actually telling the ForEach Loop to use the method WriteLine

List.ForEach(s => Console.WriteLine(s));

is actually defining a method that the foreach will call and then you are telling it what to handle there.

so for simple one liners if your method you are going to call carries the same signature as the method that gets called already I would prefer not to define the lambda, I think its a little more readable.

for methods with incompatible lambdas are definitely a good way to go, assuming they aren't to overly complicated.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.