创建一个包罗万象的处理程序,在C#中的所有事件和委托

编程入门 行业动态 更新时间:2024-10-09 15:15:27
本文介绍了创建一个包罗万象的处理程序,在C#中的所有事件和委托的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述

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

类祈求 {     公共对象调用(对象[] ARG)     {         //通用处理code     } } 静态无效的主要() {     VAR P =新的Person();     p.AddHandler(事件1,祈求新的()调用。); }

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

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

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

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

公共静态无效的AddHandler(此对象的目标,字符串字段名,     FUNC< Object []对象,对象> 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< Object []对象,对象> FUNC) {     MethodInfo的invokeMethod中= delegateType.GetMethod(调用);     类型的返回类型= invokeMethod.ReturnType;     布尔hasReturnType =的返回类型= Constants.VoidType!;     VAR paramTypes = invokeMethod.GetParameters()选择(P => 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< 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键< Object []对象,对象> FUNC)     {         VAR invokeMethod中= delegateType.GetMethod(调用);         。VAR PARMS = invokeMethod.GetParameters()选择(PARM =>防爆pression.Parameter(parm.ParameterType,parm.Name))的ToArray()。         VAR实例= func.Target == NULL?空:防爆pression.Constant(func.Target);         VAR转换= parms.Select(PARM =>防爆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#中的所有事件和委托

本文发布于:2023-11-28 08:49:34,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1641735.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:包罗万象   创建一个   事件   程序

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!