【发布时间】:2019-09-20 12:48:27
【问题描述】:
问题与此有些相关:How can I cast a delegate that takes a derived-type argument to a delegate with a base-type argument? 但我有一个动态的情况。
假设我有两个类:
class Base
{ }
class Derived : Base
{ }
static class Workers
{
public static void DoSomething(Derived obj) { ... }
}
如您所见,Workers.DoSomething 是 Action<Derived>,我想将其转换为 Action<Base>。我知道这是不安全的,但我的情况如下:我有一本字典
Dictionary<Type, Action<Base>> actions;
并基于给定的对象obj.GetType() 我检索一个动作并调用它。所以我保证在我的代码中,这样的动作会被适当的类型调用。
但这些操作显然取决于派生类型。现在链接的问题建议像
actions[typeof(Derived)] = (obj) => Workers.DoSomething((Derived)obj);
这在您在编译时知道类型的情况下是可以的。但就我而言,我通过反射检索它们。所以这里是设置
Type objType; // given
MethodInfo doSomethingMethod; // given, guaranteed to be Action<objType>
actions[objType] = // here what?
到目前为止,令人惊讶的是,我想出的最简单的解决方案是动态创建方法,如下所示:
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>));
所以我在 CIL 级别强制调用。我的真实代码稍微复杂一些,因为这些操作接受两个参数。但这只是噪音。
这很有效(并且没有演员表作为奖励)。但它看起来有点……我不知道,不安全。而且可能很难维护。有没有更好的方法来解决我的问题?
注意:我想避免doSomethingMethod.Invoke,因为它的开销很大。
注意 2:我无法控制这些类和操作。我只能检查它们。
【问题讨论】:
-
看起来还不错。您可以将其隐藏在工厂后面,按两者缓存。不是不安全,而是有点高级。