简体   繁体   中英

Delegate.CreateDelegate() creates wrong delegate when multiple methods only differ in 1 enum parameter

I have some issues creating the correct delegates for some methods of my classes. There is a base class and a derived class, both have a method protected object GetValue (EnumPID) , but EnumPID is defined separately in both classes. So in general both functions can be distinguished from each other.
Now I tried to create delegates to these functions using
Delegate.CreateDelegate (Type, object, string) ,
because new <delegate-name>(function) does not work as the function is not public.
The failure is that both delegates created by Delegate.CreateDelegate() point to the same function, which is the one from the derived class.

To simplify it, here's the test class. I found out, that it's not necessary to use inheritance, it can be reproduced with 2 different enums in the same class as well:

public class CClass
{
  public delegate void DelegatePrint1 (Enum1 i_en1);
  public delegate void DelegatePrint2 (Enum2 i_en2);
  public delegate void DelegatePrint3 (Enum3 i_en3);

  public enum Enum1 { a = 1, b = 2 }
  public enum Enum2 { a = 99 }
  public enum Enum3 { z = 100 }

  public void Print (Enum1 i_en1) { Console.WriteLine (i_en1.ToString () + "=" + (int)i_en1); }
  public void Print (Enum2 i_en2) { Console.WriteLine (i_en2.ToString () + "=" + (int)i_en2); }
  public void Print (Enum3 i_en3) { Console.WriteLine (i_en3.ToString () + "=" + (int)i_en3); }
}

private static void Main ()
{
  string sMethod_Print = "Print";
  var oClass = new CClass ();
  var delPrint1 = (CClass.DelegatePrint1)Delegate.CreateDelegate (typeof (CClass.DelegatePrint1), oClass, sMethod_Print);
  var delPrint2 = (CClass.DelegatePrint2)Delegate.CreateDelegate (typeof (CClass.DelegatePrint2), oClass, sMethod_Print);
  var delPrint3 = (CClass.DelegatePrint3)Delegate.CreateDelegate (typeof (CClass.DelegatePrint3), oClass, sMethod_Print);
  delPrint1 (CClass.Enum1.a);
  delPrint1 (CClass.Enum1.b);
  delPrint2 (CClass.Enum2.a);
  delPrint3 (CClass.Enum3.z);
}

The expected output is

a=1
b=2
a=99
z=100

The actual output is

1=1
2=2
99=99
z=100

because all delegates call the Print(Enum3) .

My questions:
1) How can I create the correct delegate? (I've already found out, see my own answer below).
2) Why does CreateDelegate (Type, object, string) create a delegate of the wrong function? Is the given Type not sufficient to determine the correct one?

Obviously the constructor of a delegate is much smarter than the Delegate.CreateDelegate() function, because
var del1 = new CClass.DelegatePrint1 (oClass.Print);
creates the correct delegate, but this only works if the function is public.

The solution is: Instead of
Delegate.CreateDelegate (Type, object, string)
you must use
Delegate.CreateDelegate (Type, object, MethodInfo)
with the MethodInfo being
var oMethod1 = typeof (CClass).GetMethod ("Print", new Type[] { typeof (CClass.Enum1) });
In this way the correct overload of Print is selected.

As to why this is necessary (which is my 2nd question), I don't have an answer. I checked out the .net source of CreateDelegate() at https://referencesource.microsoft.com/#mscorlib/system/delegate.cs,2b489eed284b305b , but I am not quite sure about how it works, because it calls external methods MulticastDelegate InternalAlloc(RuntimeType type) and bool BindToMethodName(Object target, RuntimeType methodType, String method, DelegateBindingFlags flags) . I guess that either the type information is not fully preserved by InternalAlloc , or BindToMethodName takes the Enum as an int .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM