http://www.blogcn.com/User8/flier_lu/blog/6194820.html 在上一节中曾经提到,因为 RealProxy 实现上的限制,所有需要被重定向的内部方法,都需要在一个 InternalClass 或 InternalObject 的子类中定义,以满足 MarshalByRefObject 的标记要求。同时这些方法必须以抽象方法方式定义,以便在不提供实现的情况下参与静态类型检查。
以下为引用:
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(); }
|
而 Java 的基于接口的代理模型则比这种方式要简洁得多,使用者只需要把希望获得访问能力的方法,放入一个接口中定义好,然后建立代理即可,不受讨厌的 MarshalByRefObject 限制,也可以避免在单根结构的 Java/C# 中的一些额外麻烦。
其实在 CLR 架构中完全可以模拟类似的语义,最终达到如下自动类型转换与封装的效果。无需显式注册内部类和内部对象的封装类,而是直接以接口方式任意定义组合希望访问的内部方法,由代理系统在后台自动完成类型封装和接口转换的工作。
以下为引用:
public interface IWindowsIdentityObject { string[] GetRoles(); }
IWindowsIdentityObject identity = (IWindowsIdentityObject)WrapperManager.CreateWrapperOfObject(WindowsIdentity.GetCurrent());
foreach(string role in identity.GetRoles()) { }
|
这一魔术般效果的幕后英雄就是 IRemotingTypeInfo 接口。
以下为引用:
public interface IRemotingTypeInfo { bool CanCastTo(Type fromType, object o);
string TypeName { get; set; } }
|
实现了 IRemotingTypeInfo 接口的真实代理,其创建的透明代理会在进行类型转换时,将转换请求自动转发给 IRemotingTypeInfo.CanCastTo 方法,并根据返回值来模拟真实的转换效果。
实现上,我们只需要让 ClassProxy 和 ObjectProxy 实现 IRemotingTypeInfo 接口,在其中进行是否能够转换的判断即可,最终方法调用会根据名称和参数,在每方法调用一级重新定位。
以下为引用:
internal class ObjectProxy : WrapperProxy, IRemotingTypeInfo, IInternalObject { private readonly ClassProxy _classProxy; private readonly object _object;
public ObjectProxy(ClassProxy classProxy, Type wrapperClass, object objectToProxy) : base(wrapperClass) { _classProxy = classProxy; _object = objectToProxy; }
#region IRemotingTypeInfo Members
public bool CanCastTo(Type fromType, object o) { return fromType.IsAssignableFrom(_object.GetType()) || _classProxy.CanCastTo(fromType, true); }
public string TypeName { get { return _object.GetType().FullName; } set { throw new NotSupportedException(); } }
#endregion }
|
如 ObjectProxy 在其 CanCastTo 方法中,首先判断被代理的对象是否直接实现了需转换的接口,如已经实现则直接可以允许转换,具体 Invoke 时自然能够定位到;如果被代理的对象没有实现需转换的接口,则有可能此接口可能是使用者自定义的用于访问内部方法的接口,如前面所定义的 IWindowsIdentityObject 接口。对此类自定义接口,ObjectProxy 不在对象一级进行判断,而是转交给全局唯一用于封装此内部类型的 ClassProxy 处理。
以下为引用:
internal class ClassProxy : WrapperProxy, IRemotingTypeInfo, IInternalClass { #region IRemotingTypeInfo Members
public bool CanCastTo(Type fromType, object o) { return CanCastTo(fromType, false); }
public string TypeName { get { return _classToProxy.FullName; } set { throw new NotSupportedException(); } }
#endregion }
|
ClassProxy 则在实现 IRemotingTypeInfo.CanCastTo 方法时,直接进行自定义接口的判断,因为在类这个层面,无需支持预定义接口的转换,使用者如果需要可以直接通过 InternalClass.WrappedType 方法获得被包装类型。
因此所有的自定义接口转换判断,实际上都最终落在 ClassProxy.CanCastTo 方法上。此方法将根据需转换接口的所有方法是否都在被封装类型中被实现,来判断是否允许转换到此自定义接口。而通过维护每个内部类型唯一的全局缓存,可以减少这种耗时的冗余检查操作。
以下为引用:
internal class ClassProxy : WrapperProxy, IRemotingTypeInfo, IInternalClass { private IDictionary _clsIntfs= new Hashtable(), _objIntfs = new Hashtable();
internal bool CanCastTo(Type intf, bool instance) { IDictionary intfs = instance ? _objIntfs : _clsIntfs;
lock(intf) { if(intfs.Contains(intf)) return (Boolean)intfs[intf];
bool canCast = true;
foreach(MethodInfo method in intf.GetMethods()) { if(getMethod(_classToProxy, method.Name, instance, getParamTypes(method.GetParameters())) == null) { canCast = false; break; } }
intfs[intf] = canCast;
return canCast; } } }
|
可以看到 ClassProxy 维护了内部类型唯一的 _clsIntfs 和 _objIntfs 两级缓存,分别用于缓存访问类静态方法和普通实例方法的自定义接口,而且凡是进行过转换判断的就缓存起来,以便再次转换时节省工作量。进行检测时,首先会检查缓存中是否保存了对此类型进行转换的信息,如果有则直接返回;否则会对需转换类型的每个方法,检查是否在被代理类型中存在;如果有任意一个方法不存在,则转换会失败;然后此转换的判断结果被保存到相应的缓存中,用于下次转换;最终转换结果被返回给 CLR。
具体转换调用堆栈如下:
以下为引用:
ClassProxy.CanCastTo(System.Type intf = {"NSFocus.Util.Internal.IWindowsIdentityClass"}, bool instance = false) ClassProxy.CanCastTo(System.Type fromType = {"NSFocus.Util.Internal.IWindowsIdentityClass"}, System.Object o = {System.Runtime.Remoting.Proxies.__TransparentProxy}) ...
|
而通过对 InternalClass 和 InternalObject 的扩展,可以提供显式的类型转换判断机制:
以下为引用:
internal interface IInternalClass { bool CanCastTo(Type intf); }
public abstract class InternalClass : MarshalByRefObject, IInternalClass { public abstract bool CanCastTo(Type intf); }
internal class ClassProxy : WrapperProxy, IRemotingTypeInfo, IInternalClass { public bool CanCastTo(Type intf) { return CanCastTo(intf, base.GetTransparentProxy()); } }
internal interface IInternalObject { bool CanCastTo(Type intf); }
public abstract class InternalObject : MarshalByRefObject, IInternalObject { public abstract bool CanCastTo(Type intf); }
internal class ObjectProxy : WrapperProxy, IRemotingTypeInfo, IInternalObject { public bool CanCastTo(Type intf) { return CanCastTo(intf, base.GetTransparentProxy()); } }
|
至此,自动类型转换与封装的原理与实现就大概清晰了,通过此模式,可以大大简化对内部类型访问前的薄记工作。
btw: 具体使用的单元测试代码如下:
以下为引用:
public interface IWindowsIdentityClass { IntPtr _GetCurrentToken(); }
public interface IWindowsIdentityObject { string[] GetRoles(); }
[Test] public void testAutoBinding() { InternalClass clsIdentity = WrapperManager.CreateWrapperOfType(typeof(WindowsIdentity));
Assert.IsTrue(clsIdentity.CanCastTo(typeof(IWindowsIdentityClass))); Assert.IsFalse(clsIdentity.CanCastTo(typeof(IWindowsIdentityObject)));
Assert.IsNotNull(((IWindowsIdentityClass)clsIdentity)._GetCurrentToken());
InternalObject objIdentity = WrapperManager.CreateWrapperOfObject(WindowsIdentity.GetCurrent());
Assert.IsFalse(objIdentity.CanCastTo(typeof(IWindowsIdentityClass))); Assert.IsTrue(objIdentity.CanCastTo(typeof(IWindowsIdentityObject)));
Assert.IsTrue(((IIdentity)objIdentity).IsAuthenticated); Assert.IsTrue(((IWindowsIdentityObject)objIdentity).GetRoles().Length > 0);
try { ((IWindowsIdentityObject)clsIdentity).GetRoles();;
Assert.Fail(); } catch(InvalidCastException) { }
try { ((IWindowsIdentityClass)objIdentity)._GetCurrentToken();
Assert.Fail(); } catch(InvalidCastException) { } }
|
to be continue...