Create a catch-all handler for all events and delegates in C #

I want to create a handler that can be used to handle any event or delegate. In particular, I want to write code as shown 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 that receives the name of the event and a delegate of type Func<object[], object> . He should be able to do any magic to bind the event (for example, Event1 in this case) to the delegate provided, so that the delegate is called whenever the event is fired.

The signature of Event1 should not matter, because AddHandler must work with all types of events (and delegates).

I suspect this may involve some CIL generation to create a dynamic delegate that matches the type of event specified (e.g. Event1 ) and forward the call to the specified delegate (e.g. new Invoker().Invoke ). I managed to create such a dynamic delegate, however, it could only forward static methods, not instance methods, because I could not find a way to drag the associated instance of the called method Invoker CLR stack (i.e. Invoker example in the example). See the code below to clearly see this problem (see the ISSUE line).

If anyone can point out a way to improve dynamic generation code to capture a related object or better, suggest a simpler solution that does not need CIL, then this 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); } 
+6
source share
3 answers

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

Have you considered using expression trees ( http://msdn.microsoft.com/en-us/library/bb397951.aspx )? They greatly facilitate the creation of IL.

+2
source

I developed a solution. I wrote about this with full code here if anyone is interested in the clean approach of the CIL generation (which is not as elegant as the kvb approach).

0
source

Source: https://habr.com/ru/post/885883/


All Articles