-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Description
Description
When a delegate is created from a DynamicMethod, the Method property of the resulting delegate returns a MethodInfo which does not implement CreateDelegate, leading to a NotSupportedException.
DynamicMethod's CreateDelegate implementation sets the Method property of the delegate to the DynamicMethod's RTMethodInfo object, which does not implement CreateDelegate itself. This causes problems when trying to, say, convert a delegate from one type to another type with an identical signature.
The project this was found in has for a long time has a CastDelegate helper to do exactly that. It works flawlessly in all other scenarios, including on Mono (using the Mono BCL).
Reproduction Steps
using System.Reflection.Emit;
// CastDelegate works for normal delegates
A a = () => Console.WriteLine("A");
var b = (B)CastDelegate(a, typeof(B));
a();
b();
// now lets try the same thing with a DynamicMethod
{
var dm = new DynamicMethod("dm1", typeof(void), null);
var il = dm.GetILGenerator();
il.Emit(OpCodes.Ret);
a = (A)dm.CreateDelegate(typeof(A));
}
// This invocation will throw System.NotSupportedException
b = (B)CastDelegate(a, typeof(B));
a();
b();
static Delegate CastDelegate(Delegate del, Type delType)
{
var invokeList = del.GetInvocationList();
if (invokeList.Length == 1)
return invokeList[0].Method.CreateDelegate(delType, invokeList[0].Target);
for (var i = 0; i < invokeList.Length; i++)
{
invokeList[i] = CastDelegate(invokeList[i], delType);
}
return Delegate.Combine(invokeList)!;
}
delegate void A();
delegate void B();Expected behavior
No exception, and a delegate created just as if it were called on the original DynamicMethod.
Actual behavior
When the delegate was created from a DynamicMethod, CreateDelegate throws an exception even when the call would otherwise be valid.
Regression?
No response
Known Workarounds
It is possible to reflect into the RTMethodInfo to get the DynamicMethod reference back and call CreateDelegate on that.
Configuration
This has been tested on .NET 5, 6, and 7, but likely exists on all versions of .NET Core and likely Framework.
Other information
No response