好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

“协变”、“逆变”与Delegate类型转换

“协变”、“逆变”与Delegate类型转换

我在发表了《 Delegate如何进行类型转换? 》之后又想到了其他一些相关的东西,除了简单地分析如何通过Emit实现EventHandler的类型转换之外,还加上关于Delegate“协变”与“逆变”的一些东西,算是对前一篇文章的完善。

目录 
一、从Delegate的“协变”与“逆变”说起 
二、EventHandler<TEventArgs>是否换一种定义方式更好? 
三、“统一的事件注册”能否应用于一般形式? 
四、通过Emit实现EventHandler的类型转换 

一、从Delegate的“协变”与“逆变”说起

根据Delegate“协变”与“逆变”的原理,对于两个具有相同声明的两个Delegate(A和B),如果B的所有输入(输入参数)类是A的子类或者类型相同,而A的输出(返回值、输出参数)类型是B的子类或者类型相同,那么在B能够使用的地方A也能够使用。我们在定义泛型Delegate的时候可以利用C#“协变”与“逆变”,使类型为A对象能够赋值给类型为B的变量。具体来说,我们需要将输出定义为协变体(通过out关键字),而将输入定义为逆变体(通过in关键字)。比如我们熟悉的Func<T, TResult>的定义:

    1:   public   delegate  TResult Func< in  T,  out  TResult>(T arg);

如下面的代码片断所示,自定义类型Bar是Foo的子类,那么我们就可以将一个Func<Foo, Bar> 对象赋值给Func<Bar, Foo>变量。换言之,Func<Bar, Foo>能够使用的地方,Func<Foo, Bar> 就可以使用。

    1:   class  Program
    2:  {
    3:       static   void  Main()
    4:      {
    5:          Func<Foo, Bar> getBarByFoo = foo =>  new  Bar();
    6:          Func<Bar, Foo> GetFooByBar = getBarByFoo;
    7:      }
    8:  }
    9:   class  Foo { }
   10:   class  Bar : Foo { }


二、EventHandler<TEventArgs>是否换一种定义方式更好?

事件(Event)是Delegate一项重要的使用领域,一般情况下事件成员的类型都是EventHandler。如果具有特殊的EventArgs类型,我们倾向于使用泛型的EventHandler<TEventArgs>。EventHandler和EventHandler<TEventArgs>这两个特殊的Delegate类型定义如下,两者是没有任何关系的。

    1:   public   delegate   void  EventHandler( object  sender, EventArgs e);
    2:   public   delegate   void  EventHandler<TEventArgs>( object  sender, TEventArgs e);

根据Delegate“协变”与“逆变”的原理,对于EventHandler<TEventArgs>,其实应该将作为输入参数类型的TEventArgs定义成逆变形式,像下面一样:

    1:   public   delegate   void  EventHandler<   in    TEventArgs>( object  sender, TEventArgs e)  where  TEventArgs: EventArgs;

如果是这样的话,EventHandler<EventArgs>就可以用于处理任意EventHandler<TEventArgs>类型的事件了。假设我们需要注册一个全局的EventHandler,让它在某个对象任何一个事件触发的时候被执行,如果我们能够保证所有的事件类型都是通过协变形式定义的EventHandler<TEventArgs>,我们可以按照如下的方式对目标对象的所有事件进行注册:

    1:   public   static   class  EventRegistry<T>
    2:  {
    3:       public   static   void  Register(T target,   EventHandler<EventArgs>   eventHandler)
    4:      {
    5:           foreach  (EventInfo eventInfo  in   typeof (T).GetEvents())
    6:          {
    7:              eventInfo.AddEventHandler(target, eventHandler);
    8:          }
    9:      }
   10:  }

假设我们具有如下一个类型Foo,它具有三个事件Bar、Baz和Qux,对应的类型分别是EventHandler<BarEventArgs>、EventHandler<BazEventArgs>和EventHandler<QuxEventArgs>,当方法RaiseEvents执行的时候,被注册的事件被触发。

    1:   public   class  BarEventArgs : EventArgs
    2:  { }
    3:   public   class  BazEventArgs : EventArgs
    4:  { }
    5:   public   class  QuxEventArgs : EventArgs
    6:  { }
    7:   
    8:   public   class  Foo
    9:  {
   10:       public   event    EventHandler<BarEventArgs>   Bar;
   11:       public   event    EventHandler<BazEventArgs>   Baz;
   12:       public   event    EventHandler<QuxEventArgs>   Qux;
   13:   
   14:       public   void  RaiseEvents()
   15:      {
   16:           if  ( null  != Bar) Bar( this ,  new  BarEventArgs());
   17:           if  ( null  != Baz) Baz( this ,  new  BazEventArgs());
   18:           if  ( null  != Qux) Qux( this ,  new  QuxEventArgs());
   19:      }
   20:  }

比如现在我们需要在Foo对象的任何一个事件触发的时候进行相应的日志记录,我们只需要利用上面定义的EventRegistry<T>对创建的Foo对象进行批量事件注册。

    1:   public   class  Propgram
    2:  {
    3:       static   void  Main()
    4:      {
    5:          Foo foo =  new  Foo();
    6:          EventRegistry<Foo>.Register(foo, Log);
    7:          foo.RaiseEvents();
    8:      }
    9:       static   void  Log( object  sender, EventArgs e)
   10:      {
   11:          Console.WriteLine( "{0}: {1}" , sender.GetType().Name, e.GetType().Name);
   12:      }
   13:  }


三、“统一的事件注册”能否应用于一般形式?

原则上讲,事件可以是任意类型的Delegate,但是我们使用的事件一般具有如下两个共同点:

不具有返回类型,或者返回类型为void; 有且只有两个输入参数,其一个参数类型为Object,第二个类型是EventArgs的子类。

如果事件类型对于得Delegate并没有采用逆变方式定义,那么要求我们注册一个与之 类型完全一致 的Delegate。如果我们需要将一个EventHandler对象注册给某个对象任意类型的事件,我们就不得不将注册的EventHandler转换成具体事件的类型。如下所示的时借助于类型转换的EventRegistry<T>的定义,类型转换通过调用EventHandlerConverter的Convert方法来完成。

    1:   public   static   class  EventRegistry<T>
    2:  {
    3:       public   static   void  Register(T target, EventHandler eventHandler)
    4:      {
    5:           foreach  (EventInfo eventInfo  in   typeof (T).GetEvents())
    6:          {
    7:              eventInfo.AddEventHandler(target,   EventHandlerConverter.Convert(eventHandler, eventInfo.EventHandlerType)  );
    8:          }
    9:      }
   10:  }

那么现在我们将Foo类型的三个事件类型定义成普通的Delegage:BarEventHandler、BazEventHandler和QuxEventHandler,实际上我们上面的代码依然可以执行。

    1:   public   delegate   void   BarEventHandler ( object  sender, BarEventArgs e);
    2:   public   delegate   void    BazEventHandler  ( object  sender, BazEventArgs e);
    3:   public   delegate   void    QuxEventHandler  ( object  sender, QuxEventArgs e);
    4:   
    5:   public   class  Foo
    6:  {
    7:       public   event  BarEventHandler Bar;
    8:       public   event  BazEventHandler Baz;
    9:       public   event  QuxEventHandler Qux;
   10:  }


四、通过Emit实现EventHandler的类型转换

我们通过Emit的形式实现了这个类型转换。如下面的代码片断所示,实现在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/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

分类:  [02] 编程技巧

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于“协变”、“逆变”与Delegate类型转换的详细内容...

  阅读:42次