Home > Mobile >  How and where are anonymous methods without parameters used?
How and where are anonymous methods without parameters used?

Time:08-13

When comparing anonymous methods to lambda expressions, I've seen explanations that anonymous methods provide flexibility.

Flexibility here means that you can omit parameters from anonymous methods.

// inflexible anonymous method

Action<int> action = delegate(int number) 
{ 
    Console.WriteLine($"Anonymous method: {number}"); 
}
action(1234);

// Output
// Anonymous method: 1234
// flexible anonymous method

Action<int> action = delegate 
{ 
    Console.WriteLine("Anonymous method"); 
}
action(1234);

// Output
// Anonymous method
Action<int> action = (number) => 
{ 
    Console.WriteLine($"Lambda expression: {number}"); 
};
action(1234);

// Output
// Lambda expression: 1234

However, I have two questions.

First, how does an anonymous method with an omitted parameter use the passed value? (1234 here)

Second, where do you use the flexibility that anonymous methods provide?

CodePudding user response:

I'll only answer the first question. The second question is highly opinion based, depends on context and at best I can offer incomplete / insufficient examples. Refer to the examples offered in Action Delegate. One use case is Composability

How does an anonymous method with an omitted parameter use the passed value?

It doesn't. The generated IL (intermediate language) doesn't account in any way for the provided argument. Which seems logical because if it does, it needs to have knowledge about all the callers of that method.

IL_0000:  ldsfld      UserQuery.CS$<>9__CachedAnonymousMethodDelegate1
IL_0005:  brtrue.s    IL_0018
IL_0007:  ldnull      
IL_0008:  ldftn       UserQuery.<Main>b__0
IL_000E:  newobj      System.Action<System.Int32>..ctor
IL_0013:  stsfld      UserQuery.CS$<>9__CachedAnonymousMethodDelegate1
IL_0018:  ldsfld      UserQuery.CS$<>9__CachedAnonymousMethodDelegate1
IL_001D:  stloc.0     // action
IL_001E:  ldloc.0     // action
IL_001F:  ldc.i4      D2 04 00 00 
IL_0024:  callvirt    System.Action<System.Int32>.Invoke
IL_0029:  ret         

<Main>b__0:
IL_0000:  ldstr       "Anonymous method"
IL_0005:  call        System.Console.WriteLine
IL_000A:  ret         

On IL_001F the value 1234 is pushed on the stack. In <Main>b__0 there is no "pop" from the stack. So the value is lost.

If you would have a method that takes an int, like so void test(int arg) the IL would look like:

test:
IL_0000:  ldstr       "Test method"
IL_0005:  ldarg.1     
IL_0006:  box         System.Int32
IL_000B:  call        System.Console.WriteLine
IL_0010:  ret 

Here it does "pop" the argument of the stack in IL_005.

It is worth remembering that a delegate is a generated type that derives from System.Delegate and there is an implementation detail you see on line IL_0024.

The common language runtime provides an Invoke method for each delegate type, with the same signature as the delegate. You do not have to call this method explicitly from C#, Visual Basic, or Visual C , because the compilers call it automatically.

Do note that your first and last example return the same IL for the .Net 6 compiler, which you can inspect here

  • Related