C# 为C中的所有事件和委托创建一个catch-all处理程序#

C# 为C中的所有事件和委托创建一个catch-all处理程序#,c#,.net,events,delegates,cil,C#,.net,Events,Delegates,Cil,我想创建一个可用于处理任何事件或委托的处理程序。具体来说,我希望能够编写如下代码: class Invoker { public object Invoke(object[] arg) { // generic handling code } } static void Main() { var p = new Person(); p.AddHandler("Event1", new Invoker().Invoke); } AddHa

我想创建一个可用于处理任何事件或委托的处理程序。具体来说,我希望能够编写如下代码:

class Invoker
{
    public object Invoke(object[] arg)
    {
        // generic handling code
    }
}

static void Main()
{
    var p = new Person();
    p.AddHandler("Event1", new Invoker().Invoke);
}
AddHandler
对象的扩展方法,该对象接收事件名称和
Func
类型的委托。它应该能够使用任何魔法将事件(例如,在本例中为
Event1
)绑定到提供的委托,以便在触发事件时调用委托

Event1
的签名应该无关紧要,因为
AddHandler
应该适用于所有类型的事件(和委托)

我怀疑这可能涉及一些CIL生成,以构建与指定事件类型(例如
Event1
)匹配的动态委托,并将调用转发给指定委托(例如
newinvoker().Invoke
)。我能够构建这样一个动态委托,但是它只能转发到静态方法,而不能转发到实例方法,因为我找不到将要调用的方法的绑定实例推送到CLR堆栈中的方法(即示例中的
调用程序
实例)。请参阅下面提供的代码以清楚地查看此问题(请参阅标有问题的行)

如果有人能指出一种改进动态生成代码以捕获绑定对象的方法,或者更好的方法,那么建议一种不需要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);
}
publicstaticvoidaddhandler(此对象目标,字符串字段名,
Func Func)
{
var eventInfo=target.GetType().GetEvent(字段名);
if(eventInfo!=null)
{
类型delegateType=eventInfo.EventHandlerType;
var dynamicChandler=builddynamicChandler(target.GetType(),delegateType,func);
eventInfo.GetAddMethod().Invoke(目标,新对象[]{dynamicHandler});
}
}
公共静态委托BuildDynamicHandler(此类型为delegateOwnerType,类型为delegateType,
Func Func)
{
MethodInfo invokeMethod=delegateType.GetMethod(“Invoke”);
类型returnType=invokeMethod.returnType;
bool hasReturnType=returnType!=Constants.VoidType;
var paramTypes=invokeMethod.GetParameters().Select(p=>p.ParameterType.ToArray();
var dynamicMethod=新的dynamicMethod(“添加处理程序”,
hasReturnType?returnType:null,paramTypes,delegateOwnerType);
var il=newemithelper(dynamicMethod.GetILGenerator());
if(paramTypes.Length==0)
{
il.ldnull.end();
}
其他的
{
(对象[])的类型;
il.ldc_i4(参数类型、长度);
il.newarr(对象类型);
il.stloc_0.end();
for(int i=0;i
您看过使用表达式树()吗?它们使生成IL变得更加容易。

下面是一个使用表达式树的实现:

    public static Delegate BuildDynamicHandle(Type delegateType, Func<object[], object> func)
    {
        var invokeMethod = delegateType.GetMethod("Invoke");
        var parms = invokeMethod.GetParameters().Select(parm => Expression.Parameter(parm.ParameterType, parm.Name)).ToArray();
        var instance = func.Target == null ? null : Expression.Constant(func.Target);
        var converted = parms.Select(parm => Expression.Convert(parm, typeof(object)));
        var call = Expression.Call(instance, func.Method, Expression.NewArrayInit(typeof(object), converted));
        var body =
            invokeMethod.ReturnType == typeof(void) ? (Expression)call : Expression.Convert(call, invokeMethod.ReturnType);
        var expr = Expression.Lambda(delegateType, body, parms);
        return expr.Compile();
    }
public静态委托builddynamicchandle(类型delegateType,Func-Func)
{
var invokeMethod=delegateType.GetMethod(“Invoke”);
var parms=invokeMethod.GetParameters().Select(parm=>Expression.Parameter(parm.ParameterType,parm.Name)).ToArray();
var instance=func.Target==null?null:Expression.Constant(func.Target);
var converted=parms.Select(parm=>Expression.Convert(parm,typeof(object));
var call=Expression.call(实例,funct.Method,Expression.NewArrayInit(typeof(object),converted));
变位体=
invokeMethod.ReturnType==typeof(void)?(表达式)调用:Expression.Convert(调用invokeMethod.ReturnType);
var expr=Expression.Lambda(delegateType,body,parms);
返回expr.Compile();
}

我想出了一个解决办法。我在博客上写了完整的代码,以防有人对纯CIL生成方法感兴趣(它不如kvb的方法优雅)。

你能提供一点背景说明为什么要这样做吗?@SimonC:我正在构建一种解释语言,可以与.NET互操作。我想将用该语言编写的函数连接到.NET事件,例如clrObject.someEvent+=func(){…};右边是我语言中的纯函数。一种方法是让解释器创建一个泛型处理程序,并将其添加到clrObject.someEvent中,以便稍后在触发事件时调用泛型处理程序(这将反过来使用解释器执行函数)。我不确定现在是否有替代方案。我知道exp tree。然而,我认为选择哪种代码生成机制并不重要,问题仍然是一样的。kvb的答案证明了您的正确性,我应该在考虑反射发射之前查看表达式树。表达式树方法要简单得多,如果我使用它,我可能不会遇到最初遇到的问题+1.太好了,谢谢!这比我使用的CIL方法优雅得多。我认为我需要摆脱对任何运行时代码生成任务立即使用CIL-emit的习惯。你能推荐一些有助于表达式树编程的文章、书籍和/或工具吗?@Buu-我只是通过玩API学会的。这非常简单,如果您想知道如何表达某些东西,可以随时检查C#编译器生成的表达式树。