创建一个包罗万象的处理程序,在C#中的所有事件和委托 [英] Create a catch-all handler for all events and delegates in C#

查看:173
本文介绍了创建一个包罗万象的处理程序,在C#中的所有事件和委托的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想创建一个可用于处理任何事件或委托处理程序。具体而言,我希望能写code象下面这样:

 类祈求
{
    公共对象调用(对象[] ARG)
    {
        //通用处理code
    }
}

静态无效的主要()
{
    VAR P =新的Person();
    p.AddHandler(事件1,祈求新的()调用。);
}
 

的AddHandler 对象其中接收事件的名称和类型的委托<$ C $扩展方法C>函数功能:LT;对象[],对象&gt; 。它应该能够做什么神奇的结合(在这种情况下,例如事件1 )的情况下所提供的委托,这样的委托调用每当事件。

事件1 要不要紧签名,因为的AddHandler 应与所有类型的事件(与代表)的工作

我怀疑这可能会涉及到一些CIL一代建立一个动态的委托匹配指定事件的类型(如事件1 )并转发调用指定的委托(如新祈求()调用)。我是能够建立这样一个充满活力的代表,但它只能期待静态方法,而不是实例方法,因为我无法找到一个方法来推动要被调用的方法的实例绑定到CLR堆栈(即在这个例子中祈求实例)。请参阅下面提供的看到这个问题显然是code(见标有问题的线)。

如果任何人都可以点出一个方法来改善动态生成code捕捉到绑定的对象或更好,但提出一个简单的解决方案,并不需要CIL那么它是非常AP preciated。

 公共静态无效的AddHandler(此对象的目标,字符串字段名,
    FUNC&LT; Object []对象,对象&gt; FUNC)
{
    VAR eventInfo = target.GetType()GetEvent(字段名)。
    如果(eventInfo!= NULL)
    {
        键入delegateType = eventInfo.EventHandlerType;
        VAR dynamicHandler = BuildDynamicHandler(target.GetType(),delegateType,FUNC);
        。eventInfo.GetAddMethod()调用(目标,新的对象[] {dynamicHandler});
    }
}

公共静态代表BuildDynamicHandler(这种类型的delegateOwnerType,类型delegateType,
    FUNC&LT; Object []对象,对象&gt; FUNC)
{
    MethodInfo的invokeMethod中= delegateType.GetMethod(调用);
    类型的返回类型= invokeMethod.ReturnType;
    布尔hasReturnType =的返回类型= Constants.VoidType!;
    VAR paramTypes = invokeMethod.GetParameters()选择(P =&GT; p.ParameterType)。.ToArray();
    VAR DynamicMethod的=新的DynamicMethod的(add_handler
                                            hasReturnType?返回类型:空,paramTypes,delegateOwnerType);

    变种IL =新EmitHelper(dynamicMethod.GetILGenerator());
    如果(paramTypes.Length == 0)
    {
        il.ldnull.end();
    }
    其他
    {
        il.DeclareLocal(typeof运算(对象[]));
        il.ldc_i4(paramTypes.Length);
        il.newarr(typeof运算(对象));
        il.stloc_0.end();
        的for(int i = 0; I&LT; paramTypes.Length;我++)
        {
            il.ldloc_0
                .ldc_i4㈠
                .ldarg㈠
                .boxIfValueType(paramTypes [I])
                .stelem_ref.end();
        }
        il.ldloc_0.end();
    }

    /////// ******************问题:工作静态方法只
    il.call(func.Method);
    如果(hasReturnType)
    {
        il.unbox_any(返回类型).ret();
    }
    其他
    {
        il.pop.ret();
    }
    返回dynamicMethod.CreateDelegate(delegateType);
}
 

解决方案

下面是一个使用EX pression树的实现:

 公共静态代表BuildDynamicHandle(类型delegateType,Func键&LT; Object []对象,对象&gt; FUNC)
    {
        VAR invokeMethod中= delegateType.GetMethod(调用);
        。VAR PARMS = invokeMethod.GetParameters()选择(PARM =&GT;防爆pression.Parameter(parm.ParameterType,parm.Name))的ToArray()。
        VAR实例= func.Target == NULL?空:防爆pression.Constant(func.Target);
        VAR转换= parms.Select(PARM =&GT;防爆pression.Convert(PARM的typeof(对象)));
        VAR通话=前pression.Call(例如,func.Method,防爆pression.NewArrayInit(typeof运算(对象),转换));
        VAR体=
            invokeMethod.ReturnType == typeof运算(无效)? (出pression)电话:防爆pression.Convert(打电话,invokeMethod.ReturnType);
        VAR EXPR =前pression.Lambda(delegateType,身体,PARMS);
        返回expr.Compile();
    }
 

I want to create a handler that can be used to handle any event or delegate. Specifically, I want to be able to write code like below:

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

static void Main()
{
    var p = new Person();
    p.AddHandler("Event1", new Invoker().Invoke);
}

AddHandler is an extension method for object which receive an event name and a delegate of type Func<object[], object>. It should be able to do whatever magic to bind the event (e.g. Event1 in this case) to the provided delegate so that the delegate is invoked whenever the event is fired.

The signature of Event1 shouldn't matter because AddHandler should work with all types of events (and delegates).

I suspect this might involve some CIL generation to build a dynamic delegate matching the type of the specified event (e.g. Event1) and forwarding the call to the specified delegate (e.g. new Invoker().Invoke). I was able to build such a dynamic delegate, however it could only forward to static methods, not instance methods because I couldn't find a way to push the bound instance of the to-be-invoked method into the CLR stack (i.e. the Invoker instance in the example). See the code provided below to see this issue clearly (see the line marked with ISSUE).

If anyone could point out a way to improve the dynamic generation code to capture bound object or better yet, suggest a simpler solution which doesn't need CIL then it is much appreciated.

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);
}

解决方案

Here's an implementation using expression trees:

    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();
    }

这篇关于创建一个包罗万象的处理程序,在C#中的所有事件和委托的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆