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


不使用反射

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

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 创建委托以大幅度提高反射调用的性能 中也提到过这个方法。


参考资料

.NET/C# 使用反射注册事件的更多相关文章

  1. C#-WebForm-WebForm开发基础、如何给控件注册事件?——事件委托写法、http无状态性、三层结构

    (小知识 - xml:可扩展的标记语言 html:超文本标记语言) 一.创建WebForm:新建→网站 此时文件夹中只有一个 config 文件,打开后 二.在项目下右键添加新项 在设计页面中打开 从 ...

  2. 注册事件处理程序onclick和addEventListener、attachEvent

    一.设置HTML标签属性为事件处理程序(注意和下面的设置javascript对象属性为事件处理程序是不同的) 用于设置文档元素事件处理程序属性也能化成对应的HTML标签的属性.如果这样做,属性值应该是 ...

  3. 关于javaScript注册事件传递参数的浅析

    最近这半年作为一个java 程序员,我写的javaScript代码都快比java代码多了,前段时间是给某银行做一个柜员管控系统,在柜员授权这一块功能上,由于柜员的授权需要考虑各方面的因素,比如机构权限 ...

  4. JavaScript DOM高级程序设计 4.3控制事件流和注册事件侦听器--我要坚持到底!

    一.事件流 我们通过下面一个实例,进行说明. <body> <h1>Event Flow</h1> <ul id="nav"> &l ...

  5. Android设备 cocos2dx 骨骼动画注册事件播放音效,退到后台再返回黑屏问题

    最近遇到一个cocos2dx 骨骼动画注册事件播放音效,在骨骼动画播放的时候,按HOME键退到桌面,再次打开游戏的时候,会黑屏. 解决办法如下,可能不是太完美,至少解决了大部分问题. 1.在org.c ...

  6. 为WebBrowser的WEB页的Document注册事件的问题

    原文:为WebBrowser的WEB页的Document注册事件的问题 当使用WebBrowser,并对其装载的Web页建立Document的事件后,WebBrowser里的页面元素都变得难于操作了, ...

  7. jQuery-委托事件和on方法注册事件

    delegate注册委托事件 delegate--代理.委托 事件代理----事件最终不是由$("#first")执行,它只是代理元素 第一个参数:最终发生事件的元素 第二个参数: ...

  8. JQuery的事件委托;jQuery注册事件;jQuery事件解绑

    一.事件 ①事件委托:就是给子元素的父元素或者祖先元素注册一个事件,但是事件的执行者是子元素,委托事件的好处是能够给动态创建出来时元素也加上事件. ②简单事件:就是给自己注册事件自己执行动态创建出来的 ...

  9. 利用反射绑定事件处理程序(C#)

    利用反射绑定事件处理程序(C#) 传统的写法:强类型的情况下 using System;using System.Collections.Generic;using System.Text; usin ...

随机推荐

  1. cookie—基于js的coolie使用

    //设置cookie function setCookie(name,value) { var Days = 30; var exp = new Date(); exp.setTime(exp.get ...

  2. ruby中的回调方法和钩子方法

    在ruby中,当某些特定的事件发生时,将调用回调方法和钩子方法.事件有如下几种: 调用一个不存在的对象方法 类混含一个模块 定义类的子类 给类添加一个实例方法 给对象添加一个单例方法 引用一个不存在的 ...

  3. EditPlus 4.3.2477 中文版已经发布(11月3日更新)

    新的版本修复了之前版本文本库和自动完成功能中的“^!”符号在填充项前面时不能正常工作的问题.

  4. DB开发之oracle

    常用命令: select table_name from user_tables;  //当前用户的表 select table_name from all_tables;  //所有用户的表 sel ...

  5. 支线任务2-Basic Calculator

    问题描述: 题目要求我们实现一个简单的加减计算器,计算一个表达式的值,表达式除了数字之外还可能会含有括号,加减符号以及空格. 思路: 其实看到这个题自然就会想到利用后缀式求表达式值的算法作业题,况且这 ...

  6. POJ 1386 Play on Words(欧拉路)

    http://poj.org/problem?id=1386 题意: 给出多个单词,只有单词首字母与上一个单子的末尾字母相同时可以连接,判断所有字母是否可以全部连接在一起. 思路: 判断是否存在欧拉道 ...

  7. shell 判断字符串长度是否不为0

    test.sh #!/bin/bash s1="" if test $s1 ;then echo "length is not zero" else echo ...

  8. install ros-indigo-pcl-conversions

    CMake Warning at /opt/ros/indigo/share/catkin/cmake/catkinConfig.cmake: (find_package): Could not fi ...

  9. XML_CPP_资料

    1.TinyXML解析xml文档 - zhoubl668的专栏:远帆,梦之帆! - 博客频道 - CSDN.NET.html http://blog.csdn.net/zhoubl668/articl ...

  10. 使用quartz实现定时器功能

    首先导入两个包 <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>qua ...