Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inline lambda function creation behaviour in C#

Tags:

c#

lambda

If I have a method like this

void SomeMethod () {
    Func<A,B> f =  a => /*Some code*/;
    ...
    b = f (a);
}

is f created every time SomeMethod is called? I mean, does that line take time to compute or does the compiler store the function somewhere on compile time skips it at execution?

like image 350
Cristian Garcia Avatar asked Jul 15 '14 20:07

Cristian Garcia


2 Answers

Consider this simple example:

static void Main(string[] args)
{
     Test();
     Test();
}
static void Test()
{
     Func<int, int> f = a => a * a;
     int b = f(2);
     Console.WriteLine(b);
}

The created method is:

enter image description here

And the IL code for <Test>b__0:

.method private hidebysig static int32  '<Test>b__0'(int32 a) cil managed
{
  .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) 
  // Code size       8 (0x8)
  .maxstack  2
  .locals init ([0] int32 CS$1$0000)
  IL_0000:  ldarg.0
  IL_0001:  ldarg.0
  IL_0002:  mul
  IL_0003:  stloc.0
  IL_0004:  br.s       IL_0006
  IL_0006:  ldloc.0
  IL_0007:  ret
} // end of method Program::'<Test>b__0'

IL code for Test method:

    .method private hidebysig static void  Test() cil managed
{
  // Code size       49 (0x31)
  .maxstack  2
  .locals init ([0] class [mscorlib]System.Func`2<int32,int32> f,
           [1] int32 b)
  IL_0000:  nop
  IL_0001:  ldsfld     class [mscorlib]System.Func`2<int32,int32> ConsoleApplication10.Program::'CS$<>9__CachedAnonymousMethodDelegate1'
  IL_0006:  brtrue.s   IL_001b
  IL_0008:  ldnull
  IL_0009:  ldftn      int32 ConsoleApplication10.Program::'<Test>b__0'(int32)
  IL_000f:  newobj     instance void class [mscorlib]System.Func`2<int32,int32>::.ctor(object,
                                                                                       native int)
  IL_0014:  stsfld     class [mscorlib]System.Func`2<int32,int32> ConsoleApplication10.Program::'CS$<>9__CachedAnonymousMethodDelegate1'
  IL_0019:  br.s       IL_001b
  IL_001b:  ldsfld     class [mscorlib]System.Func`2<int32,int32> ConsoleApplication10.Program::'CS$<>9__CachedAnonymousMethodDelegate1'
  IL_0020:  stloc.0
  IL_0021:  ldloc.0
  IL_0022:  ldc.i4.2
  IL_0023:  callvirt   instance !1 class [mscorlib]System.Func`2<int32,int32>::Invoke(!0)
  IL_0028:  stloc.1
  IL_0029:  ldloc.1
  IL_002a:  call       void [mscorlib]System.Console::WriteLine(int32)
  IL_002f:  nop
  IL_0030:  ret
} // end of method Program::Test

Every time you call Test method, the the cached anonymous delegate (which is Func<int,int>) is loaded, then the Invoke method is called.

So the answer is, anonymous method (<Test>b__0) is created only once by the compiler, it's not created every time you call the function.

like image 118
Selman Genç Avatar answered Sep 25 '22 22:09

Selman Genç


The C# compiler creates an ordinary (non-anonymous) method from the lambda arrow, then the delegate f will have that "generated" method on its invocation list.

Each time the SomeMethod runs, a new instance of Func<,> delegate is created which refers the ordinary (but generated) method.

It is just like:

void SomeMethod () {
    Func<A, B> f = StrangeName;
    ...
    b = f(a);
}

static B StrangeName(A a) {
    /*Some code*/
}
like image 34
Jeppe Stig Nielsen Avatar answered Sep 25 '22 22:09

Jeppe Stig Nielsen