Delegate如何進行型別轉換?

wangsys發表於2021-09-09

我們知道對於兩個不具有繼承關係的兩個型別,如果沒有為它們定義轉換器,兩這之間的型別轉換是不允許的,Delegate也是如此。但是有時候我們卻希望“相容”的兩種Delegate型別能夠進行轉換,比較典型的就是表示事件的Delegate。.NET Framework為我們定義了型別EventHandler來表示事件,但是卻沒有規定事件的Delegate型別是EventHandler的子類。原則上講,事件可以是任意型別的Delegate,但是我們使用的事件一般具有如下兩個共同點:

  • 不具有返回型別,或者返回型別為void;

  • 有且只有兩個輸入引數,其一個引數型別為Object,第二個型別是EventArgs的子類。

如果事件的型別不是EventHandler的子類,我們是不可以將一個EventHandler物件對事件進行註冊的。如果我們能夠將EventHandler物件轉換成事件對應的型別,那麼就可以到達這樣的目的:將同一個EventHandler註冊給任意的事件。我們舉個簡單的例子,假設我們具有這樣一個需求:對於指定的某個物件,需要在它每一個事件觸發的時候我們進行響應的日誌記錄。具體實現如下面的程式碼所示,具體的日誌記錄實現在Log方法中,RegisterEventHandler方法中我們透過反射的方式獲取型別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, 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/,如需轉載,請註明出處,否則將追究法律責任。

相關文章