博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Delegate如何进行类型转换?
阅读量:6719 次
发布时间:2019-06-25

本文共 5688 字,大约阅读时间需要 18 分钟。

我们知道对于两个不具有继承关系的两个类型,如果没有为它们定义转换器,两这之间的类型转换是不允许的,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 target, EventHandler eventHandler)
2: {
3:     EventInfo[] events = typeof(T).GetEvents();
4:     foreach (EventInfo eventInfo in events)
5:     {
6:         eventInfo.AddEventHandler(target, );
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, 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
(Delegate eventHandler)
73:     {
74:         return (TDelegate)(object)Convert(eventHandler, typeof(TDelegate));
75:     }
76: }

转载地址:http://gkcmo.baihongyu.com/

你可能感兴趣的文章
PHP 在大多数 Unix 平台
查看>>
革命老区江西省赣州市开发区政府暴力强拆
查看>>
extmail搭建
查看>>
FlowChart.NET中如何更改撤消或重做的属性
查看>>
【进阶篇】Android学习笔记——TextInputLayout
查看>>
python list转换字符串报错TypeError: sequence item 0: expe
查看>>
我的友情链接
查看>>
Android、iOS和Windows Phone中的推送技术
查看>>
活动目录管理工具---使用保存的查询
查看>>
MFC读写文件
查看>>
Ansible8:Playbook循环
查看>>
海淘也疯狂 跨境电商网站8月监测报告
查看>>
【51CTO学院三周年】我的在51CTO学院学习的感想
查看>>
redis+session的共享
查看>>
ls按时间排序输出文件列表
查看>>
03 bash特性
查看>>
Line in和Mic in的区别及使用
查看>>
Linux压缩和解压缩命令
查看>>
Permutations
查看>>
2.9-php使用Redis存储
查看>>