Delegate如何进行类型转换?
我们知道对于两个不具有继承关系的两个类型,如果没有为它们定义转换器,两这之间的类型转换是不允许的,Delegate也是如此。但是有时候我们却希望“兼容”的两种Delegate类型能够进行转换,比较典型的就是表示事件的Delegate。.NET Framework为我们定义了类型EventHandler来表示事件,但是却没有规定事件的Delegate类型是EventHandler的子类。原则上讲,事件可以是任意类型的Delegate,但是我们使用的事件一般具有如下两个共同点:
- 不具有返回类型,或者返回类型为void;
- 有且只有两个输入参数,其一个参数类型为Object,第二个类型是EventArgs的子类。
如果事件的类型不是EventHandler的子类,我们是不可以将一个EventHandler对象对事件进行注册的。如果我们能够将EventHandler对象转换成事件对应的类型,那么就可以到达这样的目的:将同一个EventHandler注册给任意的事件。我们举个简单的例子,假设我们具有这样一个需求:对于指定的某个对象,需要在它每一个事件触发的时候我们进行响应的日志记录。具体实现如下面的代码所示,具体的日志记录实现在Log方法中,RegisterEventHandler<T>方法中我们通过反射的方式获取类型T中定义的所有Event,并将指定的EventHandler针对这些事件进行注册。由于类型可能不一致,我们通过调用自定义的EventHandlerConverter的静态方法Convert进行类型转换。[源代码从这里下载]
1: static void RegisterEventHandler<T>(T target, EventHandler eventHandler)
2: {
3: EventInfo[] events = typeof(T).GetEvents();
4: foreach (EventInfo eventInfo in events)
5: {
6: eventInfo.AddEventHandler(target, EventHandlerConverter.Convert(eventHandler, eventInfo.EventHandlerType));
7: }
8: }
我们通过如下的代码定义了一个类型Foo,它具有Bar、Baz和Qux三个事件,其Delegate类分别是BarEventHandler、BazEventHandler和QuxEventHandler。当RaiseEvents方法被调用的时候,注册的三个事件被触发。
1: public class BarEventArgs : EventArgs
2: { }
3: public class BazEventArgs : EventArgs
4: { }
5: public class QuxEventArgs : EventArgs
6: { }
7:
8: public delegate void BarEventHandler(object sender, BarEventArgs e);
9: public delegate void BazEventHandler(object sender, BazEventArgs e);
10: public delegate void QuxEventHandler(object sender, QuxEventArgs e);
11:
12: public class Foo
13: {
14: public event BarEventHandler Bar;
15: public event BazEventHandler Baz;
16: public event QuxEventHandler Qux;
17:
18: public void RaiseEvents()
19: {
20: if (null != Bar) Bar(this, new BarEventArgs());
21: if (null != Baz) Baz(this, new BazEventArgs());
22: if (null != Qux) Qux(this, new QuxEventArgs());
23: }
24: }
现在我们在Main方法中编写如下的程序。从输出结果可以看出,同一个EventHandler是否能够成功注册给Foo中不同类型的三个事件。
1: class Program
2: {
3: static void Main(string[] args)
4: {
5: Foo foo = new Foo();
6: RegisterEventHandler<Foo>(foo, Log);
7: foo.RaiseEvents();
8: }
9:
10: static void Log(object sender, EventArgs e)
11: {
12: Console.WriteLine("{0}: {1}", sender.GetType().Name, e.GetType().Name);
13: }
14: }
输出结果:
1: Foo: BarEventArgs
2: Foo: BazEventArgs
3: Foo: QuxEventArgs
实现在EventHandlerConverter的静态方法Convert方法中的EventHandler与兼容Delegate类型之间的转换是通过“Emit”的机制实现,具体的实现逻辑如下面的代码片断所示。IsValidEventHandler方法用于验证指定的类型是否与EventHandler兼容(按照上面提及的标准进行验证),在Convert方法中我们通过Emit的方式创建了一个DynamicMethod 对象,并最终调用CreateDelegate方法将指定的Delegate对象转换成目标Delegate类型。泛型方法Convert<TDelegate>以强类型的方式指定转换的目标类型。
1: public static class EventHandlerConverter
2: {
3: public static bool IsValidEventHandler(Type eventHandlerType, out ParameterInfo[] parameters)
4: {
5: Guard.ArgumentNotNull(eventHandlerType, "eventHandlerType");
6: if (!typeof(Delegate).IsAssignableFrom(eventHandlerType))
7: {
8: parameters = new ParameterInfo[0];
9: return false;
10: }
11:
12: MethodInfo invokeMethod = eventHandlerType.GetMethod("Invoke");
13: if (invokeMethod.ReturnType != typeof(void))
14: {
15: parameters = new ParameterInfo[0];
16: return false;
17: }
18: parameters = invokeMethod.GetParameters();
19: if (parameters.Length != 2 || parameters[0].ParameterType != typeof(object))
20: {
21: return false;
22: }
23: if (!typeof(EventArgs).IsAssignableFrom(parameters[1].ParameterType))
24: {
25: return false;
26: }
27: return true;
28: }
29:
30: public static Delegate Convert(Delegate eventHandler, Type eventHandlerType)
31: {
32: Guard.ArgumentNotNull(eventHandler, "eventHandler");
33: Guard.ArgumentNotNull(eventHandlerType, "eventHandlerType");
34:
35: ParameterInfo[] destinationParameters;
36: if (!IsValidEventHandler(eventHandlerType, out destinationParameters))
37: {
38: throw new InvalidOperationException();
39: }
40:
41: if (eventHandler.GetType() == eventHandlerType)
42: {
43: return eventHandler;
44: }
45:
46: ParameterInfo[] sourceParameters;
47: if (!IsValidEventHandler(eventHandler.GetType(), out sourceParameters))
48: {
49: throw new InvalidOperationException();
50: }
51: Type[] paramTypes = new Type[destinationParameters.Length + 1];
52: paramTypes[0] = eventHandler.GetType();
53: for (int i = 0; i < destinationParameters.Length; i++)
54: {
55: paramTypes[i + 1] = destinationParameters[i].ParameterType;
56: }
57: DynamicMethod method = new DynamicMethod("WrappedEventHandler", null, paramTypes);
58: MethodInfo invoker = paramTypes[0].GetMethod("Invoke");
59: ILGenerator il = method.GetILGenerator();
60: il.Emit(OpCodes.Ldarg_0);
61: il.Emit(OpCodes.Ldarg_1);
62: il.Emit(OpCodes.Ldarg_2);
63: if (!sourceParameters[1].ParameterType.IsAssignableFrom(destinationParameters[1].ParameterType))
64: {
65: il.Emit(OpCodes.Castclass, sourceParameters[1].ParameterType);
66: }
67: il.Emit(OpCodes.Call, invoker);
68: il.Emit(OpCodes.Ret);
69: return method.CreateDelegate(eventHandlerType, eventHandler);
70: }
71:
72: public static TDelegate Convert<TDelegate>(Delegate eventHandler)
73: {
74: return (TDelegate)(object)Convert(eventHandler, typeof(TDelegate));
75: }
76: }作者:Artech 出处:http://artech.cnblogs.com/
转载于:https://www.cnblogs.com/CLR010/archive/2012/11/05/2755057.html
Delegate如何进行类型转换?相关推荐
- 不具有继承关系的Delegate如何进行类型转换?
- 引自:Artech 我们知道对于两个不具有继承关系的两个类型,如果没有为它们定义转换器,两这之间的类型转换是不允许的,Delegate也是如此.但是有时候我们却希望"兼容"的两 ...
- C#调用C++的DLL 所有数据类型转换方式
本以为这篇搜集整理的代码会是很不错的文章,花了一天时间,搜索到最后居然出来一篇叫做"C# 与 C++ 数据类型对照表"的文章.几乎囊括掉和大部分的数据了,太打击我了. 本文中有部分 ...
- iOS 13 Xcode11 中的 Scene Delegate
如果将Xcode更新到11, 创建项目.默认会创建SceneDelegate.swift, 那么问题来了, 这个代理用来干嘛的了? 在这篇文章中,我们将探索iOS13和Xcode11的改变.我们着重介 ...
- 转:C#调用C++的DLL搜集整理的所有数据类型转换方式
//C++中的DLL函数原型为 //extern "C" __declspec(dllexport) bool 方法名一(const char* 变量名1, uns ...
- 三种属性操作性能比较:PropertyInfo + Expression Tree + Delegate.CreateDelegate
在<上篇>中,我比较了三种属性操作的性能:直接操作,单纯通过PropertyInfo反射和IL Emit.本篇继续讨论这个话题,我们再引入另外两种额外的属性操作方式:Expression ...
- C#调用VC的DLL的接口函数参数类型转换一览表
handle---------IntPtr hwnd-----------IntPtr char *----------string int * -----------ref int int & ...
- 在C++中实现委托(Delegate)
在C++中实现委托(Delegate) 标签: C++设计模式 2016-03-18 21:04 494人阅读 评论(1) 收藏 举报 分类: C/C++(166) 设计模式(28) ...
- java基本类型转换,随记
java基本类型转换: double double 转 long double random = Math.round(Math.random()*10000); long l = new Doubl ...
- Go 知识点(12) — 类型转换以三方库 cast
类型转换在编程语言中是很常见的操作,在 Go 语言中其类型转换有下面一些注意点. 1. 整数类型之间的转换 对于整数类型转换,原则上目标类型的取值范围要包含被转换值,也就是说要转换类型的值取值范围要小 ...
- 数据类型转换pytorch
du = torch.ones([2,2]) a = np.array([[1,2],[3,4]],dtype=np.float32) b = torch.from_numpy(a)#数据类型是不变的 ...
最新文章
- 2019年度CSDN博客之星TOP10榜单揭晓,你上榜了吗?
- html去除边角,WEB开发向HTML5及CSS3迈进(1)——圆框边角的处理
- matlab 基础知识查漏
- Linux搭建安卓开发环境
- VMware Workstation卸载清理批处理命令
- C/C++中函数参数传递
- devc++鼠标变成了光标_Excel填充别再用鼠标拖拉了!用这4个方法,效率至少高10倍!...
- 多线程下不能用truncate吗_那么多的化妆品,怀孕后都不能用了吗?
- python的输入来源包括网络输入法_python可以调用计算机上的输入法进行输入吗?比如调用输入法在其他程序的输入框中写上字符串...
- Mac安装MATLAB 2017b
- Fragstats计算景观格局指数(初学指南)
- 《统计学》第八版贾俊平第八章假设检验知识点总结及课后习题答案
- 7-1 前n项的和1 (10 分)
- 多年珍藏的Android开发必备网站和工具
- 如何选择字体(font-family)
- 《整洁代码之道》学习书摘(二)第一章——整洁代码
- C++:应用有限差分法求解随时间变化 平流方程 ut = - c * ux 在一个空间维度上,与 恒定速度,使用FTCS方法,正向时间差, 居中空间差(附完整源码)
- 2022 IEEE Fellow:AI华人之光
- risc-v vector扩展1.0版本解读(riscv-v-spec-1.0)
- java-非对称(RSA)签名加密(springboot框架)