c# 创建委托 消息订阅,C#中的动态事件订阅

这篇博客探讨了如何使用C#的表达式树动态地订阅事件,即使不知道事件处理程序所需的委托签名。通过创建lambda表达式并利用Delegate.CreateDelegate方法,可以实现对任意类型事件的订阅,从而在事件触发时执行指定操作,如打印到控制台。
摘要由CSDN通过智能技术生成

How would you dynamically subscribe to a C# event so that given a Object instance and a String name containing the name of the event, you subscribe to that event and do something (write to the console for example) when that event has been fired?

It would seem using Reflection this isn't possible and I would like to avoid having to use Reflection.Emit if possible, as this currently (to me) seems like the only way of doing it.

/EDIT: I do not know the signature of the delegate needed for the event, this is the core of the problem

/EDIT 2: Although delegate contravariance seems like a good plan, I can not make the assumption necessary to use this solution

解决方案

You can compile expression trees to use void methods without any arguments as event handlers for events of any type. To accommodate other event handler types, you have to map the event handler's parameters to the events somehow.

using System;

using System.Linq;

using System.Linq.Expressions;

using System.Reflection;

class ExampleEventArgs : EventArgs

{

public int IntArg {get; set;}

}

class EventRaiser

{

public event EventHandler SomethingHappened;

public event EventHandler SomethingHappenedWithArg;

public void RaiseEvents()

{

if (SomethingHappened!=null) SomethingHappened(this, EventArgs.Empty);

if (SomethingHappenedWithArg!=null)

{

SomethingHappenedWithArg(this, new ExampleEventArgs{IntArg = 5});

}

}

}

class Handler

{

public void HandleEvent() { Console.WriteLine("Handler.HandleEvent() called.");}

public void HandleEventWithArg(int arg) { Console.WriteLine("Arg: {0}",arg); }

}

static class EventProxy

{

//void delegates with no parameters

static public Delegate Create(EventInfo evt, Action d)

{

var handlerType = evt.EventHandlerType;

var eventParams = handlerType.GetMethod("Invoke").GetParameters();

//lambda: (object x0, EventArgs x1) => d()

var parameters = eventParams.Select(p=>Expression.Parameter(p.ParameterType,"x"));

var body = Expression.Call(Expression.Constant(d),d.GetType().GetMethod("Invoke"));

var lambda = Expression.Lambda(body,parameters.ToArray());

return Delegate.CreateDelegate(handlerType, lambda.Compile(), "Invoke", false);

}

//void delegate with one parameter

static public Delegate Create(EventInfo evt, Action d)

{

var handlerType = evt.EventHandlerType;

var eventParams = handlerType.GetMethod("Invoke").GetParameters();

//lambda: (object x0, ExampleEventArgs x1) => d(x1.IntArg)

var parameters = eventParams.Select(p=>Expression.Parameter(p.ParameterType,"x")).ToArray();

var arg = getArgExpression(parameters[1], typeof(T));

var body = Expression.Call(Expression.Constant(d),d.GetType().GetMethod("Invoke"), arg);

var lambda = Expression.Lambda(body,parameters);

return Delegate.CreateDelegate(handlerType, lambda.Compile(), "Invoke", false);

}

//returns an expression that represents an argument to be passed to the delegate

static Expression getArgExpression(ParameterExpression eventArgs, Type handlerArgType)

{

if (eventArgs.Type==typeof(ExampleEventArgs) && handlerArgType==typeof(int))

{

//"x1.IntArg"

var memberInfo = eventArgs.Type.GetMember("IntArg")[0];

return Expression.MakeMemberAccess(eventArgs,memberInfo);

}

throw new NotSupportedException(eventArgs+"->"+handlerArgType);

}

}

static class Test

{

public static void Main()

{

var raiser = new EventRaiser();

var handler = new Handler();

//void delegate with no parameters

string eventName = "SomethingHappened";

var eventinfo = raiser.GetType().GetEvent(eventName);

eventinfo.AddEventHandler(raiser,EventProxy.Create(eventinfo,handler.HandleEvent));

//void delegate with one parameter

string eventName2 = "SomethingHappenedWithArg";

var eventInfo2 = raiser.GetType().GetEvent(eventName2);

eventInfo2.AddEventHandler(raiser,EventProxy.Create(eventInfo2,handler.HandleEventWithArg));

//or even just:

eventinfo.AddEventHandler(raiser,EventProxy.Create(eventinfo,()=>Console.WriteLine("!")));

eventInfo2.AddEventHandler(raiser,EventProxy.Create(eventInfo2,i=>Console.WriteLine(i+"!")));

raiser.RaiseEvents();

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值