Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implicit method group conversion gotcha

Tags:

c#

delegates

I am wondering why the output of the given code (execute it in LinqPad)

void Main() {
    Compare1((Action)Main).Dump();
    Compare2(Main).Dump();
}

bool Compare1(Delegate x) {
    return x == (Action)Main;
}

bool Compare2(Action x) {
    return x == Main;
}

is always:

 False
 True

I have naively expected it to be True in both cases.

like image 377
m0sa Avatar asked Jan 20 '12 08:01

m0sa


2 Answers

This is how it looks when compiled to IL and then decompiled back to C#. Note that in both cases there's new Action(Main) - a new reference object (delegate) with pointer to actual method stored inside.

private static void Main()
{
    Program.Compare1(new Action(Program.Main)).Dump();
    Program.Compare2(new Action(Program.Main)).Dump();
    Console.ReadLine();
}

private static bool Compare1(Delegate x)
{
   return x == new Action(Program.Main);
}

private static bool Compare2(Action x)
{
   return x == new Action(Program.Main);
}

If then we take a look into CIL, the former uses ceq (reference comparison) and the latter uses call bool [mscorlib]System.Delegate::op_Equality(class [mscorlib]System.Delegate, class [mscorlib]System.Delegate) to compare delegates.

First returns false because actions wrapping your delegates are two distinct reference objects.

Second returns true as the equality operator implemented on the Delegate class compares actual targets inside wrappers (actions).

like image 105
Wiktor Zychla Avatar answered Oct 22 '22 12:10

Wiktor Zychla


The false result is related to the fact that the Compare1() method performs reference comparison on two different object (compilator shows the corresponding warning):

IL_0001:  ldarg.0
IL_0002:  ldnull
IL_0003:  ldftn      instance void ConsoleApplication1.Test::Main()
IL_0009:  newobj     instance void [System.Core]System.Action::.ctor(object,
                                                                   native int)
IL_000e:  ceq <<reference comparison

You can avoid the issue using the following code:

bool Compare1(Delegate x) {
    return x == (Delegate)((Action)Main);
}
like image 28
DmitryG Avatar answered Oct 22 '22 11:10

DmitryG