Implicit method group conversion gotcha

2020-08-19 02:39发布

问题:

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.

回答1:

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).



回答2:

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);
}


标签: c# delegates