专栏首页walterlv - 吕毅的博客.NET/C# 使用反射注册事件

.NET/C# 使用反射注册事件

.NET/C# 使用反射注册事件

2018-09-01 12:31

使用反射,我们可以很容易地在运行时调用一些编译时无法确定的属性、方法等。那么如何注册事件呢?

本文将介绍如何使用反射注册事件。


不使用反射

例如,我们希望反射的类型是这样的:

public class Walterlv
{
    public event EventHandler BlogPublished;
}

那么只需要使用如下代码即可完成事件的注册:

var walterlv = new Walterlv();
walterlv += Walterlv_BlogPublished;
public void Walterlv_BlogPublished(object sender, EventHandler handler)
{
}

使用反射

而如果使用反射,则是:

var walterlv = new Walterlv();
var eventInfo = typeof(Walterlv).GetEvent(nameof(BlogPublished));
var handler = new EventHandler(Walterlv_BlogPublished);
eventInfo.AddEventHandler(walterlv, handler);

当然,实际使用的时候,如果能访问到 Walterlv 类型,当然也不会去用到反射,所以通常情况是这样的:

public void AddHandler<T>(T instance, string eventName, EventHandler handler)
{
    var eventInfo = instance.GetType().GetEvent(eventName);
    eventInfo.AddEventHandler(instance, handler);
}

安全地使用反射

虽然以上方式使用了反射成功注册了事件,但实际上我们的参数中传入了一个特定类型的委托 EventHandler。实际上事件的委托种类非常多。

在委托中,即便签名完全相同,也不是同一个委托类型。如果传入的参数类型改为 EventHandler<EventArgs>,或者 BlogPublished 事件的类型改为 EventHandler<EventHandler>,虽然实际上这两个委托的签名是兼容的,但其委托类型不同,依然是不能互相转换的。你会在运行时遇到一下异常:

▲ 委托无法转换

所以我们必须有一些更安全的方式来注册事件。

正常情况下,我们转换一个签名兼容的委托是使用构造函数:

public EventHandler ConvertDelegate(EventHandler<EventArgs> handler)
{
    return new EventHandler(handler);
}

那么在反射中,我们需要使用 Delegate.CreateDelegate 创建指定类型的委托。

public void AddHandler<T>(T instance, string eventName)
{
    var eventInfo = instance.GetType().GetEvent(eventName);
    var methodInfo = GetType().GetMethod(nameof(Walterlv_BlogPublished));
    var @delegate = Delegate.CreateDelegate(eventInfo.EventHandlerType, this, methodInfo);
    eventInfo.AddEventHandler(instance, @delegate);
}

public void Walterlv_BlogPublished(object sender, EventHandler handler)
{
}

这里,Delegate.CreateDelegate 的作用就是执行委托类型的转换。我在 .NET Core/Framework 创建委托以大幅度提高反射调用的性能 中也提到过这个方法。


参考资料

本文会经常更新,请阅读原文: https://walterlv.com/post/add-event-handler-using-reflection.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。

本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名 吕毅 (包含链接: https://walterlv.com ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系 (walter.lv@qq.com)

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 解读 Microsoft.NET.Sdk 的源码,你能定制各种奇怪而富有创意的编译过程

    发布于 2018-06-30 05:55 更新于 2018-08...

    walterlv
  • 使用不安全代码将 Bitmap 位图转为 WPF 的 ImageSource 以获得高性能和持续小的内存占用

    发布于 2017-11-09 15:25 更新于 2017-11...

    walterlv
  • WPF 让普通 CLR 属性支持 XAML 绑定(非依赖属性),这样 MarkupExtension 中定义的属性也能使用绑定了

    如果你写了一个 MarkupExtension 在 XAML 当中使用,你会发现你在 MarkupExtension 中定时的属性是无法使用 XA...

    walterlv
  • C#嵌套任务和子任务

      任务嵌套就是指在一个任务中又创建了一个任务,而这个新建的任务,就被称为子任务。在没有特殊声明的情况下,父子任务是一起运行的,如SimpleNestedTas...

    Kiba518
  • 高校人工智能教育“开荒”,还得靠这三步

    其中一本是关于人工智能深度学习的《深度学习导论与应用实践》,这本书由中国科学院大学深度学习领域的学者编著,清华大学出版社出版。

    罗超频道
  • 一触知万物,AI用抓手判别物体 | 一周AI新闻

    加州大学戴维斯分校的研究人员和卡内基梅隆大学的研究人员合作开发了一种方法,让一款实验性的软体机器人抓手也拥有了 “品尝” 能力,他们使用了经过基因工程改造的细菌...

    代码医生工作室
  • 回环检测与建图

    最简单方法:对任意两个关键帧进行特征匹配 基于里程计的方法(Odometry based) 基于外观的方法(Appearance based)

    小飞侠xp
  • Tron(波场)实践篇

    官方文档:https://github.com/tronprotocol/Documentation/tree/master/%E4%B8%AD%E6%96%8...

    陨石坠灭
  • 想要让世界温暖一点点

    小App大爱心,专为听障人士设计的心声想要让世界温暖一点点 手机普及了,人与人之间的交流方便了;微信问世了,动动手指就能发段语音表达你的想法。不过,这些对于聋哑...

    ThoughtWorks
  • 云教室桌面云虚拟化瘦客户机解决方案

    随着信息技术的高速发展,对电脑主机性能要求越来越高,老旧电脑主机的改造只解燃眉之急,并非长久之计,还是得新建计算机教室以满足教学需要。针对经费不足,无法...

    nc云终端yun0101.com

扫码关注云+社区

领取腾讯云代金券