基于透明代理的内部类访问抽象1-类型与调用的封装
2008-08-16 17:32
465 查看
按照 MS 的通常思维习惯,.NET 在自带的类库中保留了很多被标记为 internal 的工具类。为了更简洁或更直接的达到目的,我们往往需要牺牲一定的兼容性,通过调用这些内部类来简化我们的工作。甚至有些工作不通过内部类根本无法完成,如我以前在一篇《正确判断当前用户角色》文章中使用的方法,就可以在 hack 一下内部实现后,大大减轻调试和测试的工作量,而通过正规方式实现非常繁琐。
但是按照标准方法通过 Reflection 调用内部类及其方法,编写代码极其繁琐;而且因为大量操作是通过非强类型的字符串或对象数组,造成很多问题只有到运行时才能发现;同时因为重复代码到处散步,代码的可测试性也很差。
如《正确判断当前用户角色》一文中获取角色名称的函数调用代码如下:
这是一段非常典型的通过反射调用内部方法的实现代码,需要通过 GetMethod 获取方法,再手工 Invoke,最后将返回值强制转换。为了解决这个问题,我们希望能够通过某种方式,以强类型的预定义方法定义跨越 internal 限制,最终达到如下代码的效果:
注意这里所有的方法、参数和返回值都是在编译时参与静态类型检查的,最终封装结果能够象普通对象方法调用一样,直接调用 internal 或 private 的方法、属性、甚至静态方法。而这一看似魔术般的封装,后台实现是完全依赖于标准的透明代理机制。
关于透明代理的原理和实现机制我这里就不再复述,前者可以google到成堆的资料,后者可以参考我另外两篇底层实现机制的分析文章:
用WinDbg探索CLR世界 [10] 透明代理实现原理浅析
用WinDbg探索CLR世界 [10] 透明代理实现原理浅析 - 静态结构
首先我们来看看上面那段代码,完成这个内部方法的封装,功能上可以分为四个步骤:
1. 定义内部类型的封装抽象类 WindowsIdentityObject
2. 将抽象类注册到系统 WrapperManager.RegisterWrapper
3. 构造对象的封装类实例 WrapperManager.CreateWrapperOfObject
4. 通过封装类调用对象的内部方法 identity.GetRoles
1. 定义内部类型的封装抽象类
因为设计上有调用内部类静态函数的需求,因此需要把内部类封装为 InternalClass 和 InternalObject 两级。前者负责封装内部类型,并提供静态函数的调用支持;后者则封装内部对象,提供普通方法的调用支持。
从 MarshalByRefObject 对象继承是透明代理的要求,实际上就如我在《透明代理实现原理浅析》系列文章中分析的那样,MarshalByRefObject 实际上只是起到一个标记的作用,告诉 CLR 不要对此对象的方法调用进行优化,以便透明代理能够通过方法表接管所有的访问。而因为这个限制,导致此方案必须使用抽象类型,而非语法上更自然的接口来定义内部方法,这方面 Java 的基于接口的要更加灵活一些。
对使用者来说,只需要通过类似 WindowsIdentityObject 的方式,从这两个类型派生新的封装类,即可获得自动的调用支持。例如 CLR 内部实现了一个 System.Variant 类型,用于可变类型参数的访问:
这里随便选取了一个静态函数、一个类方法和一个属性,要完成对他们的封装,只需要定义 VariantClass 和 VariantObject 两个类型。
2. 将抽象类注册到系统
在合适的时候,将这两个类型注册到系统,以完成对内部类型 System.Variant 的封装。系统在注册的时候,会为类型建立相应的透明代理,以提供后面需要获得的类型层面的封装支持。实现细节等会讨论具体代码时再详细解释。
3. 构造对象的封装类实例
构造封装类的实例可以在类的层面通过 WrapperManager.CreateWrapperOfType 方法来进行;或者在获取类型的封装后,通过它的 InternalClass.CreateInstance 方法来进行;如果需要对现有实例进行包装,则可以通过 WrapperManager.CreateWrapperOfObject 或 InternalClass.CreateWrapperOfObject 方法来进行。
为了提供封装类一级的 CreateInstance 等方法,必须对 InternalClass 进行扩展,等会讨论具体代码时再详细解释这种实现是如何运作的。
所有这些构造,实际上都是在获取指定封装类的透明代理,为最终的方法调用做准备。
4. 通过封装类调用对象的内部方法
获得了内部类和内部对象的封装类实例后,就可以通过透明代理,以普通的方法调用语法来调用静态或实例的方法了。
所有的方法调用,都会被透明代理,转发给内部类的相应方法来实现。
在了解了使用层面的基本情况后,我们来看看整个封装系统的后台是如何运作的。
首先,WrapperManager 类型维护了一个以内部类型为键、内部类的封装类实例为值的类型包装映射字典,每种类型只能注册一套封装类。
所有的封装类构造请求都会先查看是否在缓存中命中,如果没有才去创建新的自动封装支持。而内部对象的封装类创建,则转交给内部类型的封装类去完成。
这样一来,WrapperManager 类型就可以专心于全局封装映射表的维护,而无需担心具体的封装实现。内部类 ClassProxy 会搞定剩下的一切,包括对内部类的类型封装、对内部对象的封装等等。WrapperManager 所需做的仅仅是告诉 ClassProxy 如何去封装,并最终获取封装类实例返回给最终使用者。
注意这里可以忽略内部类和内部对象的特定封装,因为有些情况下是不需要静态方法或对象方法的调用的,此时系统会使用缺省的封装类来进行抽象。
而专注于内部类和内部对象封装的 ClassProxy 和 ObjectProxy 类,都是从抽象封装代理类 WrapperProxy 集成而来的,层次结构如下:
+-- ClassProxy
RealProxy <-- WrapperProxy <--+
+-- ObjectProxy
WrapperProxy 类通过工厂方法模式,提供具体实现无关的方法调用封装机制,也就是最关键的 RealProxy.Invoke 方法,此方法中具体完成对内部类和内部对象的方法的调用。
通过透明代理发起的方法调用,会被 RealProxy 封装成 IMessage 类型的调用消息。因此第一步工作就是从消息中将方法调用的相关信息解析出来。
IMethodCallMessage.Args 中保存了方法被调用时的参数数组,通过这个信息,我们可以解析得到目标方法的参数类型数组。getParamTypes 方法完成具体的类型信息获取工作。
而根据方法的名称和调用参数类型数组,可以通过抽象方法 getMethod 获得实际的方法信息实例,类似于 C++ 中函数指针的概念。因为内部类和内部对象的方法调用目标不同,这里定义了一个抽象 getMethod 方法来屏蔽此区别。
可以看到 ClassProxy 和 ObjectProxy 实际上是通过 WrapperProxy.getMethod 工具方法,分别从被封装的类型和对象中,尝试获取方法实例的。而 out 类型参数 target,则可以告诉 WrapperProxy.Invoke 在进行实际方法调用时,调用的目标是谁。对内部类的封装类来说,这里需要填入 null 来完成调用。
在获取方法信息后,调用方法就很直接了。通过 MethodInfo.Invoke 调用,捕获可能发生的异常。如果调用成功则构造 ReturnMessage 消息返回调用结果;否则构造 ReturnMessage 消息返回异常信息。注意这里捕获到的异常,都是通过 TargetInvocationException 异常封装过的,因此需要使用 e.InnerException 来解码得到实际发生的异常。
对于为调用内部类方法而定义的封装类抽象方法来说,如上的代码就足够了。但我们为 InternalClass 等封装增加了 CreateInstance 等等特定的支持,这就需要通过一个小技巧来转发调用了。因为对于 InternalClass 和 InternalObject 的子类,他们都必须是不可能初始化的抽象类,在里面定义方法的实现是没有意义的。我们必须在真实代理这一段,模拟透明代理自身的方法实现,而非简单将之转发给被代理的对象。
为此可将这些特定方法抽象成新的接口,如
定义 IInternalClass 和 IInternalObject 接口的作用,仅仅是保障透明代理和真实代理的实现一致性,因此是可选的。
然后在真实代理中实现相应函数,并修改 WrapperProxy.Invoke 的方法获取算法,如
新增的代码可以在被封装类型中不存在调用方法时,从真实代理自身来查询方法实现。这样一来对 InternalClass.CreateInstance 的调用,就会因为被封装类型不存在此方法,而被转发到 ClassProxy.CreateInstance 方法上。这个技巧在完成对透明代理的功能增补时非常有用,可以进一步用来模拟 COM 中接口一级的组合语义,以后有机会我再写文章详细讨论。
可以看到 ClassProxy.CreateInstance 实际上是通过调用参数,寻找合适的构造函数来创建内部对象,并通过 ClassProxy.CreateWrapperOfObject 方法对其进行封装。
至此,基于透明代理的内部类访问抽象中,对类型与调用的封装就基本介绍完毕,下一节会详细讨论进一步完成对 out/ref 参数、delegate 等语法特性的模拟与封装机制,以及如何通过此机制进行功能编码简化。
to be continue...
但是按照标准方法通过 Reflection 调用内部类及其方法,编写代码极其繁琐;而且因为大量操作是通过非强类型的字符串或对象数组,造成很多问题只有到运行时才能发现;同时因为重复代码到处散步,代码的可测试性也很差。
如《正确判断当前用户角色》一文中获取角色名称的函数调用代码如下:
以下为引用: WindowsIdentity identity = WindowsIdentity.GetCurrent(); MethodInfo method = identity.GetType().GetMethod("GetRoles", BindingFlags.Instance | BindingFlags.NonPublic); String[] roleNames = (String[])method.Invoke(identity, new object[] {}); foreach(string roleName in roleNames) { Console.WriteLine(roleName); } |
以下为引用: public abstract class WindowsIdentityObject : InternalObject { public abstract string[] GetRoles(); } WrapperManager.RegisterWrapper(typeof(WindowsIdentity).FullName, null, typeof(WindowsIdentityObject)); WindowsIdentityObject identity = (WindowsIdentityObject)WrapperManager.CreateWrapperOfObject(WindowsIdentity.GetCurrent()); foreach(string role in identity.GetRoles()) { Console.WriteLine(roleName); } |
关于透明代理的原理和实现机制我这里就不再复述,前者可以google到成堆的资料,后者可以参考我另外两篇底层实现机制的分析文章:
用WinDbg探索CLR世界 [10] 透明代理实现原理浅析
用WinDbg探索CLR世界 [10] 透明代理实现原理浅析 - 静态结构
首先我们来看看上面那段代码,完成这个内部方法的封装,功能上可以分为四个步骤:
1. 定义内部类型的封装抽象类 WindowsIdentityObject
2. 将抽象类注册到系统 WrapperManager.RegisterWrapper
3. 构造对象的封装类实例 WrapperManager.CreateWrapperOfObject
4. 通过封装类调用对象的内部方法 identity.GetRoles
1. 定义内部类型的封装抽象类
因为设计上有调用内部类静态函数的需求,因此需要把内部类封装为 InternalClass 和 InternalObject 两级。前者负责封装内部类型,并提供静态函数的调用支持;后者则封装内部对象,提供普通方法的调用支持。
以下为引用: public abstract class InternalClass : MarshalByRefObject { } public abstract class InternalObject : MarshalByRefObject { } |
对使用者来说,只需要通过类似 WindowsIdentityObject 的方式,从这两个类型派生新的封装类,即可获得自动的调用支持。例如 CLR 内部实现了一个 System.Variant 类型,用于可变类型参数的访问:
以下为引用: public internal struct Variant { internal static int GetCVTypeFromClass(Type ctype); public uint ToUInt32() public bool IsEmpty { get; } } |
以下为引用: public abstract class VariantClass : InternalClass { public const int CV_I4 = 8; public static readonly string CLASS_NAME = "System.Variant"; public abstract int GetCVTypeFromClass(Type ctype); } public abstract class VariantObject : InternalObject { public abstract bool IsEmpty { get; } public abstract uint ToUInt32(); } |
在合适的时候,将这两个类型注册到系统,以完成对内部类型 System.Variant 的封装。系统在注册的时候,会为类型建立相应的透明代理,以提供后面需要获得的类型层面的封装支持。实现细节等会讨论具体代码时再详细解释。
以下为引用: WrapperManager.RegisterWrapper(VariantClass.CLASS_NAME, typeof(VariantClass), typeof(VariantObject)); |
构造封装类的实例可以在类的层面通过 WrapperManager.CreateWrapperOfType 方法来进行;或者在获取类型的封装后,通过它的 InternalClass.CreateInstance 方法来进行;如果需要对现有实例进行包装,则可以通过 WrapperManager.CreateWrapperOfObject 或 InternalClass.CreateWrapperOfObject 方法来进行。
以下为引用: VariantClass clsVar = (VariantClass)WrapperManager.CreateWrapperOfType(VariantClass.CLASS_NAME); VariantObject objVar = (VariantObject)clsVar.CreateInstance(15); WindowsIdentityObject identity = (WindowsIdentityObject)WrapperManager.CreateWrapperOfObject(WindowsIdentity.GetCurrent()); |
以下为引用: public abstract class InternalClass : MarshalByRefObject { public abstract InternalObject CreateInstance(params object[] args); public abstract InternalObject CreateWrapperOfObject(object obj); } |
4. 通过封装类调用对象的内部方法
获得了内部类和内部对象的封装类实例后,就可以通过透明代理,以普通的方法调用语法来调用静态或实例的方法了。
以下为引用: Assert.AreEqual(clsVar.GetCVTypeFromClass(typeof(Int32)), VariantClass.CV_I4); Assert.IsFalse(objVar.IsEmpty); Assert.AreEqual(objVar.ToUInt32(), 15); foreach(string role in identity.GetRoles()) { } |
在了解了使用层面的基本情况后,我们来看看整个封装系统的后台是如何运作的。
首先,WrapperManager 类型维护了一个以内部类型为键、内部类的封装类实例为值的类型包装映射字典,每种类型只能注册一套封装类。
以下为引用: public class WrapperManager { private static IDictionary _wrappers = new Hashtable(); private WrapperManager() { } public static void RegisterWrapper(Type typeToWrap, Type classWrapper, Type objectWrapper) { _wrappers.Add(typeToWrap, CreateWrapperOfType(typeToWrap, classWrapper == null ? typeof(InternalClass) : classWrapper, objectWrapper == null ? typeof(InternalObject) : objectWrapper)); } public static void RegisterWrapper(string typeToWrap, Type classWrapper, Type objectWrapper) { RegisterWrapper(Type.GetType(typeToWrap), classWrapper, objectWrapper); } public static void UnregisterWrapper(Type typeToWrap) { _wrappers.Remove(typeToWrap); } public static void UnregisterWrapper(String typeToWrap) { UnregisterWrapper(Type.GetType(typeToWrap)); } } |
以下为引用: public class WrapperManager { public static InternalClass CreateWrapperOfType(Type typeToWrap) { InternalClass wrapper = (InternalClass)_wrappers[typeToWrap]; if(wrapper == null) { wrapper = CreateWrapperOfType(typeToWrap, null, null); _wrappers[typeToWrap] = wrapper; } return wrapper; } public static InternalClass CreateWrapperOfType(String typeToWrap) { return CreateWrapperOfType(Type.GetType(typeToWrap)); } public static InternalObject CreateWrapperOfObject(object objectToWrap) { InternalClass wrapper = CreateWrapperOfType(objectToWrap.GetType()); return wrapper.CreateWrapperOfObject(objectToWrap); } } |
以下为引用: public class WrapperManager { protected static InternalClass CreateWrapperOfType(Type typeToWrap, Type classWrapper, Type objectWrapper) { ClassProxy proxy = new ClassProxy(typeToWrap, classWrapper == null ? typeof(InternalClass) : classWrapper, objectWrapper == null ? typeof(InternalObject) : objectWrapper); return (InternalClass)proxy.GetTransparentProxy(); } } |
而专注于内部类和内部对象封装的 ClassProxy 和 ObjectProxy 类,都是从抽象封装代理类 WrapperProxy 集成而来的,层次结构如下:
+-- ClassProxy
RealProxy <-- WrapperProxy <--+
+-- ObjectProxy
WrapperProxy 类通过工厂方法模式,提供具体实现无关的方法调用封装机制,也就是最关键的 RealProxy.Invoke 方法,此方法中具体完成对内部类和内部对象的方法的调用。
以下为引用: internal abstract class WrapperProxy : RealProxy { protected WrapperProxy(Type classToProxy) : base(classToProxy) { } public override IMessage Invoke(IMessage msg) { // 完成方法调用 } } |
以下为引用: internal abstract class WrapperProxy : RealProxy { protected Type[] getParamTypes(object[] objs) { Type[] types = new Type[objs.Length]; for(int i=0; i<types.Length; i++) { types[i] = objs[i] == null ? null : objs[i].GetType(); } return types; } public override IMessage Invoke(IMessage msg) { if (msg is IMethodCallMessage) { IMethodCallMessage msgCall = (IMethodCallMessage)msg; Type[] types = getParamTypes(msgCall.Args); // 完成方法调用 } else { Debug.Assert(false, "Unknown message type " + msg.GetType().FullName, msg.ToString()); } return null; } } |
以下为引用: internal abstract class WrapperProxy : RealProxy { protected abstract MethodInfo getMethod(string name, Type[] types, out object target); public override IMessage Invoke(IMessage msg) { // 获取方法调用参数类型信息 Object target; MethodInfo method = getMethod(msgCall.MethodName, types, out target); if(method == null) throw new InvalidOperationException(string.Format("Method {0} is not found", msgCall.MethodName)); // 完成方法调用 } } |
以下为引用: internal abstract class WrapperProxy : RealProxy { protected static readonly BindingFlags DefaultBindingFlags = BindingFlags.Public | BindingFlags.NonPublic; protected static readonly BindingFlags ConstructorBindingFlags = DefaultBindingFlags | BindingFlags.Instance; protected static readonly BindingFlags MethodBindingFlags = DefaultBindingFlags; protected MethodInfo getMethod(Type type, string name, bool instance, Type[] types) { BindingFlags flags = MethodBindingFlags | (instance ? BindingFlags.Instance : BindingFlags.Static); return type.GetMethod(name, flags, null, types, null); } } internal class ClassProxy : WrapperProxy, IInternalClass { private readonly Type _classToProxy, _objectWrapper; internal ClassProxy(Type classToProxy, Type classWrapper, Type objectWrapper) : base(classWrapper) { _classToProxy = classToProxy; _objectWrapper = objectWrapper; } protected override MethodInfo getMethod(string name, Type[] types, out object target) { target = null; return getMethod(_classToProxy, name, false, types); } } internal class ObjectProxy : WrapperProxy, IInternalObject { private readonly object _object; public ObjectProxy(Type wrapperClass, object objectToProxy) : base(wrapperClass) { _object = objectToProxy; } protected override MethodInfo getMethod(string name, Type[] types, out object target) { target = _object; return getMethod(target.GetType(), name, true, types); } } |
以下为引用: internal abstract class WrapperProxy : RealProxy { protected abstract MethodInfo getMethod(string name, Type[] types, out object target); public override IMessage Invoke(IMessage msg) { // 获取方法调用参数类型信息 // 获取方法信息 try { return new ReturnMessage(method.Invoke(target, msgCall.Args), null, 0, null, msgCall); } catch(Exception e) { return new ReturnMessage(e.InnerException, msgCall); } } } |
对于为调用内部类方法而定义的封装类抽象方法来说,如上的代码就足够了。但我们为 InternalClass 等封装增加了 CreateInstance 等等特定的支持,这就需要通过一个小技巧来转发调用了。因为对于 InternalClass 和 InternalObject 的子类,他们都必须是不可能初始化的抽象类,在里面定义方法的实现是没有意义的。我们必须在真实代理这一段,模拟透明代理自身的方法实现,而非简单将之转发给被代理的对象。
为此可将这些特定方法抽象成新的接口,如
以下为引用: internal interface IInternalClass { InternalObject CreateInstance(params object[] args); InternalObject CreateWrapperOfObject(object obj); Type WrappedType { get; } } public abstract class InternalClass : MarshalByRefObject, IInternalClass { public abstract InternalObject CreateInstance(params object[] args); public abstract InternalObject CreateWrapperOfObject(object obj); public abstract Type WrappedType { get; } } internal interface IInternalObject { object WrappedObject { get; } } public abstract class InternalObject : MarshalByRefObject, IInternalObject { public abstract object WrappedObject { get; } } |
以下为引用: internal class ObjectProxy : WrapperProxy, IInternalObject { private readonly object _object; #region IInternalObject Members public object WrappedObject { get { return _object; } } #endregion } |
以下为引用: internal abstract class WrapperProxy : RealProxy { protected abstract MethodInfo getMethod(string name, Type[] types, out object target); public override IMessage Invoke(IMessage msg) { // 获取方法调用参数类型信息 Object target; MethodInfo method = getMethod(msgCall.MethodName, types, out target); if(method == null) { target = this; method = getMethod(target.GetType(), msgCall.MethodName, true, types); } if(method == null) throw new InvalidOperationException(string.Format("Method {0} is not found", msgCall.MethodName)); // 完成方法调用 } } |
以下为引用: internal class ClassProxy : WrapperProxy, IInternalClass { #region IInternalClass Members public InternalObject CreateInstance(params object[] args) { if(args == null) throw new ArgumentNullException("args" ; ConstructorInfo ctor = _classToProxy.GetConstructor(ConstructorBindingFlags, null, getParamTypes(args), null); if(ctor == null) throw new InvalidOperationException("Not a match constructor to create object." ; return CreateWrapperOfObject(ctor.Invoke(args)); } public InternalObject CreateWrapperOfObject(object obj) { ObjectProxy proxy = new ObjectProxy(_objectWrapper, obj); return (InternalObject)proxy.GetTransparentProxy(); } public Type WrappedType { get { return _classToProxy; } } #endregion } 引用:/article/4792321.html |
至此,基于透明代理的内部类访问抽象中,对类型与调用的封装就基本介绍完毕,下一节会详细讨论进一步完成对 out/ref 参数、delegate 等语法特性的模拟与封装机制,以及如何通过此机制进行功能编码简化。
to be continue...
相关文章推荐
- 基于透明代理的内部类访问抽象 [1] 类型与调用的封装
- 基于透明代理的内部类访问抽象 [2] 自动类型转换与封装
- 基于透明代理的内部类访问抽象2-自动类型转换与封装
- 利用.NET Remoting基础架构中的真实代理/透明代理技术实现了不针对具体类型、具体方法的通用方法调用拦截机制
- 基于反射机制的服务代理调用
- Castle 系列: Castle DynamicProxy动态生成透明代理类型
- .NET对存储过程的调用抽象封装
- 下载指定代理文件调用IE使用代理访问指定网站的代码
- Nginx反向代理(基于目录动静分离、不同浏览器类型不同代理、基于扩展名的不同代理)
- 阿里 SLB +nginx 基于$http_x_forwarded_for 代理IP 访问控制
- C#在父窗口中调用子窗口的过程(无法访问已释放的对象)异常,不存在从对象类型System.Windows.Forms.DateTimePicker到已知的托管提供程序本机类型的映射。
- 基于stringstream的类型转换封装
- j2ee访问oracle数据库封装类以及调用方法
- delphi不同版本字符串类型的演化(要支持基于firemonkey的app调用,字符串最好使用olevariant类型)
- Java中的内部类02-访问其所在方法中的final类型的局部变量
- 在iOS开发中调用本地plist文件并封装为NSDictionary字典类型
- 错误: 从内部类中访问本 地变量vvv; 需要被声明为最终类型
- 基于JDK动态代理实现的接口链式调用(Fluent Interface)工具
- squid透明代理和访问控制
- MySql基于ADO.NET方式访问数据库ADOHelper封装MySqlHelper