The question is somewhat related to this: How can I cast a delegate that takes a derived-type argument to a delegate with a base-type argument? but I have a dynamic situation.
So lets say I have two classes:
class Base
{ }
class Derived : Base
{ }
static class Workers
{
public static void DoSomething(Derived obj) { ... }
}
As you can see Workers.DoSomething
is Action<Derived>
and I want to cast it to Action<Base>
. I know this is unsafe but my case is as follows: I keep a dictionary
Dictionary<Type, Action<Base>> actions;
and based on given objects obj.GetType()
I retrieve one action and call it. And so I guarantee in my code that such action will be called with an appropriate type.
But those actions depend on the derived type obviously. Now the linked question suggests something like
actions[typeof(Derived)] = (obj) => Workers.DoSomething((Derived)obj);
This is ok in a situation when you know types at compile time. But in my case I retrieve them via reflection. So here's the setup
Type objType; // given
MethodInfo doSomethingMethod; // given, guaranteed to be Action<objType>
actions[objType] = // here what?
So far, surprisingly, the simplest solution I came up with is to create the method dynamically like follows:
Type objType; // given
MethodInfo doSomethingMethod; // given
var dynamicMethod = new DynamicMethod(
$"Dynamic{doSomethingMethod.Name}",
typeof(void),
new Type[] { typeof(Base) },
typeof(Base).Module
);
var il = dynamicMethod.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.EmitCall(OpCodes.Callvirt, doSomethingMethod, null);
il.Emit(OpCodes.Ret);
actions[objType] = (Action<Base>)dynamicMethod
.CreateDelegate(typeof(Action<Base>));
And so I force the call at CIL level. My real code is slightly more complicated since those actions accept two parameters. But that's just noise.
This works (and there's no cast as a bonus). But it looks kind of... I don't know, unsafe. And probably hard to maintain. Is there a better way to solve my problem?
Note: I want to avoid doSomethingMethod.Invoke
due to its significant overhead.
Note 2: I have no control over those classes and actions. I can only inspect them.