“协变”、“逆变”与Delegate类型转换
我在发表了《 Delegate如何进行类型转换? 》之后又想到了其他一些相关的东西,除了简单地分析如何通过Emit实现EventHandler的类型转换之外,还加上关于Delegate“协变”与“逆变”的一些东西,算是对前一篇文章的完善。
目录
一、从Delegate的“协变”与“逆变”说起
一、从Delegate的“协变”与“逆变”说起
二、EventHandler<TEventArgs>是否换一种定义方式更好?
三、“统一的事件注册”能否应用于一般形式?
四、通过Emit实现EventHandler的类型转换根据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 Program2: {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 : EventArgs2: { }3: public class BazEventArgs : EventArgs4: { }5: public class QuxEventArgs : EventArgs6: { }7:8: public class Foo9: {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 Propgram2: {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 Foo6: {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 EventHandlerConverter2: {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类型转换的详细内容...
声明:本文来自网络,不代表【好得很程序员自学网】立场,转载请注明出处:http://haodehen.cn/did47682