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: }

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏木宛城主

SharePoint CAML In Action——Part II

在SharePoint中,相对于Linq to SharePoint而言,CAML是轻量化的。当然缺点也是显而易见的,"Hard Code"有时会让你抓狂。在实...

1995
来自专栏程序员的SOD蜜

不使用反射的实体类方案

看过很多ORM的实体类方案,大多是用反射来读数据库数据,这样当频繁操作实体类的时候效率很低,我借鉴了一位朋友的思路,采用.NET 2.0的泛型技术,为实体类提供...

2198
来自专栏行者常至

019.数据交换格式:Json、XML

数据交换格式中,最核心的就是Json和XML。 其中,Json是一种轻量级数据交换格式,XML是一种重量级的数据交换格式。 相比于xml这种数据交换格式来说...

1793
来自专栏java架构师

Stream篇(1)

最近在看一个写的很好的博客,为了加深记忆,把自认为重要的东西,一边看,一边记在这里 一、什么是流、字节序列、字节 一条河中有一条鱼游过,这条鱼就是一个字节,这个...

3037
来自专栏风口上的猪的文章

.NET面试题系列[14] - LINQ to SQL与IQueryable

"理解IQueryable的最简单方式就是,把它看作一个查询,在执行的时候,将会生成结果序列。" - Jon Skeet

1491
来自专栏张善友的专栏

Dynamite动态排序库

易于使用和高性能动态排序库支持类似 SQL 语法和嵌套/复杂的表达式,使用 System.Linq.Expression 动态生成快速比较器。 使用此库就可以使...

22510
来自专栏风口上的猪的文章

.NET面试题系列[13] - LINQ to Object

"C# 3.0所有特性的提出都是更好地为LINQ服务的" - Learning Hard

1202
来自专栏DOTNET

C#要点补充

1字符串与时间的互转 DateTime.TryParse将空字符串、为null或格式不正确,则转换为的DateTime所代表的值为:0001/1/1 0:00...

2915
来自专栏大内老A

Enterprise Library深入解析与灵活应用(9):个人觉得比较严重的关于CachingCallHandler的Bug

微软EnterLib的Policy Injection Application Block(PIAB)是一个比较好用的轻量级的AOP框架,你可以通过创建自定义的...

2109
来自专栏逸鹏说道

C# 温故而知新:Stream篇(三)

TextWriter 和 StreamWriter 目录: 为何介绍TextWriter? TextWriter的构造,常用属性和方法 IFormatProvi...

3347

扫码关注云+社区

领取腾讯云代金券