是否通过动态生成的CIL代码将委托强制转换为动态参数类型



问题与此有一定关系:如何将接受派生类型参数的委托强制转换为具有基类型参数的代理?但我的处境充满活力。

假设我有两个类:

class Base
{ }
class Derived : Base
{ }
static class Workers
{
public static void DoSomething(Derived obj) { ... }
}

如您所见,Workers.DoSomethingAction<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:我无法控制这些类和操作。我只能检查它们。

您似乎意识到您正在颠倒协方差和反方差的规则,但这里有一些相当整洁的东西可能适用于您描述的情况(您也可以检查(b as Derived) != null以确定):

class Base { }
class Derived : Base { }
static class Workers 
{ 
public static void DoSomething(Derived obj) { Console.WriteLine("Test"); } 
}
class Program
{
static Dictionary<Type, Action<Base>> actions;
//  *** Note use of dummy to avoid having to know T at compile time and T : Base constraint
//  (compiler can't infer T from Action<T> alone, this way runtime works out T from given object instance)...
static void AddAction<T>(T dummy, Action<T> a) where T : Base 
{
actions.Add(typeof(T), b => a(b as T));
}
static void Main(string[] args)
{
actions = new Dictionary<Type, Action<Base>>();
var o = new Derived();  //  the object you get "from elsewhere"
AddAction(o, Workers.DoSomething);
actions[o.GetType()](o);
Console.ReadKey();
}
}

希望这是有用的。(非常好奇"为什么?"不过;-)

我不能声称这是我的想法,但在这里找到了:https://stackoverflow.com/a/32702091/1848953在研究你的问题时。第一:

private static Action<object> ConvertDelegateToAction<T>(Delegate d) { return obj => ((Action<T>)d)((T)obj); }
private static readonly MethodInfo CastMethodInfo = typeof(Program).GetMethod(nameof(ConvertDelegateToAction), BindingFlags.Static | BindingFlags.NonPublic);

使用:

public static Action<object> GetActionT(Type t, Delegate d) { return (Action<object>)CastMethodInfo.MakeGenericMethod(t).Invoke(null, new object[] { d }); }

我觉得挺整洁的。您在运行时GetType()并使用Invoke(),但这只是为了提前获得Action<object>。(我没有使用基类,而是使用了一个空接口。看起来工作得很好。)

让我们知道这是否令人满意。

相关内容

  • 没有找到相关文章

最新更新