使用反射向事件添加处理程序,动态处理

时间:2012-11-12 08:37:49

标签: c# .net events reflection event-handling

我正在编写一个框架,其中一个功能涉及远程处理事件(我认为其中的细节现在不相关)。但是,在给定目标对象引用和事件名称的情况下,需要将事件处理程序挂钩到任何事件。

处理事件的方法将封送/以其他方式将参数代理到某些处理程序代码,然后将返回类型强制转换为事件处理程序的返回类型。实际上,它包装了处理程序。

我认为,由于事件处理程序可能需要具有任意类型的任意数量的参数,因此处理按需挂钩事件的请求的唯一方法是使用reflection / emit(代码生成) )构建自定义代理方法。我发布这个问题来检查这个假设,看看是否有人有任何其他/更好的想法。

目前,我创建了一个DynamicMethod,然后执行此类操作。它几乎可以工作 - 但是这个动态代理方法调用的包装器方法必须是静态的(声明几乎是public static object GenericHandler(object[] parameters));我不希望它。所以我当前的任务是使用此方法动态创建整个类型,还有一个字段来存储对象的实例以调用包装器。但是,这就是重点(我认为) - 我想检查一下我对创建动态代码的假设是否正确。但是,对于参考:

        // obj = target object, eventName = event name
        var evtInfo = obj.GetType().GetEvent(eventName);

        var handlerType = evtInfo.EventHandlerType;

        var eventInvokeMethod = handlerType.GetMethod("Invoke");
        var paramTypes = eventInvokeMethod.GetParameters().Select(p => p.ParameterType).ToArray();
        var returnType = eventInvokeMethod.ReturnType;

        var handlerMethod = new DynamicMethod(
            "evtHandler_" + eventName,
            MethodAttributes.Static | MethodAttributes.Public,
            CallingConventions.Standard,
            returnType,
            paramTypes,
            typeof(my container class).Module,
            false);

        var il = handlerMethod.GetILGenerator();

        // locals: [0] = parameter array
        il.DeclareLocal(typeof(object[]));

        // create an appropriately-sized array objects, for the parameters
        il.Emit(OpCodes.Ldc_I4, (int)paramTypes.Count());
        il.Emit(OpCodes.Newarr, typeof(object));
        il.Emit(OpCodes.Stloc_0);

        for (int i = 0; i < paramTypes.Length; i++)
        {
            // for the array: load the parameter into the same index of the array
            il.Emit(OpCodes.Ldloc_0);
            il.Emit(OpCodes.Ldc_I4, i);
            il.Emit(OpCodes.Ldarg, i);
            if (!paramTypes[i].IsClass)
            {
                // box the value first..
                il.Emit(OpCodes.Box, paramTypes[i]);
            }
            il.Emit(OpCodes.Stelem_Ref);
        }

        // call our generic handler with the array of parameters, then convert the return
        // value to the target type via unboxing or a cast
        il.Emit(OpCodes.Ldloc_0);
        il.Emit(OpCodes.Call, wrapper.GetMethodInfo());
        if (!returnType.IsClass)
        {
            il.Emit(OpCodes.Unbox_Any, returnType);
        }
        else
        {
            il.Emit(OpCodes.Castclass, returnType);
        }
        il.Emit(OpCodes.Ret);

如果没有回复,我得到了包装类型工作的上述理论,我会发布更新的代码..但是如果他们在那里,我很乐意听到这种方法的替代方法。

1 个答案:

答案 0 :(得分:0)

有必要实现(并改进)上述代码。使用动态生成的程序集,类型和方法,我可以将触发的任何事件代理为常规object HandleEvent(object[] parameters)方法。