Вопрос в некоторой степени связан с этим: Как я могу привести делегата, который получает аргумент производного типа, к делегату с аргументом базового типа? , но у меня динамическая ситуация.
Допустим, у меня есть два класса:
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: У меня нет контроля над этими классами и действиями.Я могу только осмотреть их.