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