Я хочу создать обработчик, который можно использовать для обработки любого события или делегата. В частности, я хочу иметь возможность писать код, как показано ниже:
class Invoker
{
public object Invoke(object[] arg)
{
// generic handling code
}
}
static void Main()
{
var p = new Person();
p.AddHandler("Event1", new Invoker().Invoke);
}
AddHandler
- это метод расширения для object
, который получает имя события и делегат типа Func<object[], object>
. Он должен иметь возможность использовать любую магию, чтобы связать событие (например, Event1
в данном случае) с предоставленным делегатом, чтобы делегат вызывался всякий раз, когда событие вызывается.
Подпись Event1
не должна иметь значения, поскольку AddHandler
должна работать со всеми типами событий (и делегатами).
Я подозреваю, что это может потребовать некоторого поколения CIL для создания динамического делегата, соответствующего типу указанного события (например, Event1
) и перенаправления вызова указанному делегату (например, new Invoker().Invoke
). Мне удалось создать такой динамический делегат, однако он мог только перенаправлять статические методы, а не методы экземпляра, потому что я не мог найти способ вставить связанный экземпляр вызываемого метода в стек CLR (т. Е. Invoker
экземпляр в примере). См. Приведенный ниже код, чтобы ясно увидеть эту проблему (см. Строку, помеченную ISSUE).
Если кто-то может указать способ улучшения кода динамической генерации для захвата связанного объекта или еще лучше, предложите более простое решение, не требующее CIL, тогда оно очень ценится.
public static void AddHandler(this object target, string fieldName,
Func<object[], object> func)
{
var eventInfo = target.GetType().GetEvent(fieldName);
if (eventInfo != null)
{
Type delegateType = eventInfo.EventHandlerType;
var dynamicHandler = BuildDynamicHandler(target.GetType(), delegateType, func);
eventInfo.GetAddMethod().Invoke(target, new Object[] { dynamicHandler });
}
}
public static Delegate BuildDynamicHandler(this Type delegateOwnerType, Type delegateType,
Func<object[], object> func)
{
MethodInfo invokeMethod = delegateType.GetMethod("Invoke");
Type returnType = invokeMethod.ReturnType;
bool hasReturnType = returnType != Constants.VoidType;
var paramTypes = invokeMethod.GetParameters().Select(p => p.ParameterType).ToArray();
var dynamicMethod = new DynamicMethod("add_handler",
hasReturnType ? returnType : null, paramTypes, delegateOwnerType);
var il = new EmitHelper(dynamicMethod.GetILGenerator());
if (paramTypes.Length == 0)
{
il.ldnull.end();
}
else
{
il.DeclareLocal(typeof(object[]));
il.ldc_i4(paramTypes.Length);
il.newarr(typeof(object));
il.stloc_0.end();
for (int i = 0; i < paramTypes.Length; i++)
{
il.ldloc_0
.ldc_i4(i)
.ldarg(i)
.boxIfValueType(paramTypes[i])
.stelem_ref.end();
}
il.ldloc_0.end();
}
/////// ****************** ISSUE: work for static method only
il.call(func.Method);
if (hasReturnType)
{
il.unbox_any(returnType).ret();
}
else
{
il.pop.ret();
}
return dynamicMethod.CreateDelegate(delegateType);
}