Action to Delegate : new Action or casting Action?

15,920

Solution 1

There is no difference between this two instruction. In the both instruction, a new instance of Action is created.

The IL code below seems to confirm this.

Console Program :

class Program
{
    static void Main(string[] args)
    {
        Delegate barInit = (Action)(() => DoNothing());
        Delegate fooInit = new Action(() => DoNothing());
    }

    private static void DoNothing() { }
}

IL Code :

// First instruction
IL_0000: ldsfld class [mscorlib]System.Action CodeMachineTest.Program::'CS$<>9__CachedAnonymousMethodDelegate2'
IL_0005: brtrue.s IL_0018

IL_0007: ldnull
IL_0008: ldftn void CodeMachineTest.Program::'<Main>b__0'()

// Create a new Action instance for the instruction (Action)(() => DoNothing())
IL_000e: newobj instance void [mscorlib]System.Action::.ctor(object, native int)

IL_0013: stsfld class [mscorlib]System.Action CodeMachineTest.Program::'CS$<>9__CachedAnonymousMethodDelegate2'

IL_0018: ldsfld class [mscorlib]System.Action CodeMachineTest.Program::'CS$<>9__CachedAnonymousMethodDelegate2'
IL_001d: pop

// Second instruction
IL_001e: ldsfld class [mscorlib]System.Action CodeMachineTest.Program::'CS$<>9__CachedAnonymousMethodDelegate3'
IL_0023: brtrue.s IL_0036

IL_0025: ldnull
IL_0026: ldftn void CodeMachineTest.Program::'<Main>b__1'()
IL_002c: newobj instance void [mscorlib]System.Action::.ctor(object, native int)
IL_0031: stsfld class [mscorlib]System.Action CodeMachineTest.Program::'CS$<>9__CachedAnonymousMethodDelegate3'

IL_0036: ldsfld class [mscorlib]System.Action CodeMachineTest.Program::'CS$<>9__CachedAnonymousMethodDelegate3'
IL_003b: pop
IL_003c: ret

Solution 2

To my mind there is no difference.

new Action(() => DoNothing(param));

This just creates a new Action and passes along a Lambda expression, which the compiler will deal with and see to it that everything is wired up just fine.

(Action)(() => DoNothing(param));

This works because a lambda method such as this returns no value and takes no parameters, as such the compiler can verify that it is "mappable" to an Action on the grounds that it goes through the delegate system.

They are more-or-less one and the same, depending on any sort of compiler optimisations it's hard to say which is more performant, perhaps you should test the performance and see for yourself?

It's an interesting question and an exploration into the delegation system and how Linq and Expressions fit in.

new Func<string>(() => "Boo!");

is more-or-less equivalent to:

(Func<String>)() => "Boo!";

As far as I'm aware they both end up going down through the delegate system think Invoke() etc, it would be interesting if you did test the performance and shared your results.

Share:
15,920
Hyralex
Author by

Hyralex

French in Auckland !

Updated on June 09, 2022

Comments

  • Hyralex
    Hyralex almost 2 years

    I found two different ways to initialize a Delegate with an Action :

    Create a new action or casting to Action.

    Delegate foo = new Action(() => DoNothing(param));
    Delegate bar = (Action)(() => DoNothing(param));
    

    Is there a difference between this 2 syntaxes?

    Which one is better and why?

    Delegate is use in this example because the syntaxes is useful to call methods like BeginInvoke or Invoke with a lambda expression, and it's important to cast the lambda expression into an action

    static main 
    {
        Invoke((Action)(() => DoNothing())); // OK
        Invoke(new Action(() => DoNothing())); // OK
        Invoke(() => DoNothing()); // Doesn't compil
    }
    
    private static void Invoke(Delegate del) { }
    

    But it's interesting to see that the compiler authorized this :

    Action action = () => DoNothing();
    Invoke(action);
    
  • Hyralex
    Hyralex almost 11 years
    I did some test and there is no difference. After disassembly, the IL and machine code are the same.
  • Hyralex
    Hyralex almost 11 years
    Some methods like Invoke or BeginInvoke need a Delegate, and it's necessary to cast or create an Action to use lambda expression.
  • Jeppe Stig Nielsen
    Jeppe Stig Nielsen almost 11 years
    @Hyralex But all concrete delegate types, including System.Action, derive (via System.MulticastDelegate) from System.Delegate. Therefore the methods Invoke and BeginInvoke and other members are inherited. So you could say e.g. Action bar = () => DoNothing(param); bar.BeginInvoke( ... );. So I don't understand.
  • Hyralex
    Hyralex almost 11 years
    I'm talking about BeginInvoke and Invoke of existing object like Dispatcher. I edited my question to put more example.
  • Jeppe Stig Nielsen
    Jeppe Stig Nielsen almost 11 years
    It's not particularly interesting, they are just two syntaxes that lead to the same IL. So this has no relation to performance, so what do you want him to share? Read the C# Language Specification if you want details. The first expression is in 7.6.10.5 Delegate creation expressions, and the second one is in 6.5 Anonymous function conversions. Section numbers are from version 5.0 of the document. Note that 7.6.10.5 clearly says that it is processed the same way as in 6.5.
  • Jeppe Stig Nielsen
    Jeppe Stig Nielsen almost 11 years
    (This comment replaces previous comment.) I was wrong about Invoke and BeginInvoke. They don't exist on System.Delegate since the signature is not known at that "level". So they are not inherited, they are declared on the concrete type System.Action (and any other concrete delegate type). The method DynamicInvoke, on the other hand, is an example of an inherited method.