Delegate如何進行型別轉換?
我們知道對於兩個不具有繼承關係的兩個型別,如果沒有為它們定義轉換器,兩這之間的型別轉換是不允許的,Delegate也是如此。但是有時候我們卻希望“相容”的兩種Delegate型別能夠進行轉換,比較典型的就是表示事件的Delegate。.NET Framework為我們定義了型別EventHandler來表示事件,但是卻沒有規定事件的Delegate型別是EventHandler的子類。原則上講,事件可以是任意型別的Delegate,但是我們使用的事件一般具有如下兩個共同點:
不具有返回型別,或者返回型別為void;
有且只有兩個輸入引數,其一個引數型別為Object,第二個型別是EventArgs的子類。
如果事件的型別不是EventHandler的子類,我們是不可以將一個EventHandler物件對事件進行註冊的。如果我們能夠將EventHandler物件轉換成事件對應的型別,那麼就可以到達這樣的目的:將同一個EventHandler註冊給任意的事件。我們舉個簡單的例子,假設我們具有這樣一個需求:對於指定的某個物件,需要在它每一個事件觸發的時候我們進行響應的日誌記錄。具體實現如下面的程式碼所示,具體的日誌記錄實現在Log方法中,RegisterEventHandler
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, 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, 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
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
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: }
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/3244/viewspace-2805190/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- '2'>'10'==true? JS是如何進行隱式型別轉換的?JS型別
- Java 8型別轉換及改進Java型別
- 型別轉換型別
- java- 型別-轉換:基本型別以及包裝型別的轉換Java型別
- 資料型別,型別轉換資料型別
- MySQL把字串欄位轉換為日期型別進行比較MySql字串型別
- 如何實現隱式型別轉換型別
- js型別轉換JS型別
- 型別轉換(cast)型別AST
- Convert型別轉換型別
- Java資料型別及型別轉換Java資料型別
- JNI常用型別轉換型別
- 容器,型別轉換。List。型別
- c++ 型別轉換C++型別
- interface{} 型別的轉換型別
- 型別轉換注意點型別
- 變數型別轉換變數型別
- Spring型別轉換(Converter)Spring型別
- 資料型別轉換資料型別
- golang的型別轉換Golang型別
- 型別轉換運算子型別
- C# 型別轉換C#型別
- 型別轉換工具類型別
- 強制型別轉換型別
- go interface{}型別轉換Go型別
- 第11章 使用類——型別轉換(二)將自定義型別轉換為內建型別型別
- mysql bigint型別和datetime型別的轉換MySql型別
- JavaScript隱式型別轉換JavaScript型別
- 強制型別轉換之(==)型別
- JavaScript 資料型別轉換JavaScript資料型別
- 型別轉換(int 和 String)型別
- Pytorch變數型別轉換PyTorch變數型別
- javascript資料型別轉換JavaScript資料型別
- 【Java】資料型別轉換Java資料型別
- go-常用型別轉換Go型別
- java中的型別轉換Java型別
- C++ 常型別轉換C++型別
- 資料型別及轉換資料型別