http://www.blogcn.com/User8/flier_lu/blog/6194820.html
在上一节中曾经提到,因为 RealProxy 实现上的限制,所有需要被重定向的内部方法,都需要在一个 InternalClass 或 InternalObject 的子类中定义,以满足 MarshalByRefObject 的标记要求。同时这些方法必须以抽象方法方式定义,以便在不提供实现的情况下参与静态类型检查。
而 Java 的基于接口的代理模型则比这种方式要简洁得多,使用者只需要把希望获得访问能力的方法,放入一个接口中定义好,然后建立代理即可,不受讨厌的 MarshalByRefObject 限制,也可以避免在单根结构的 Java/C# 中的一些额外麻烦。
其实在 CLR 架构中完全可以模拟类似的语义,最终达到如下自动类型转换与封装的效果。无需显式注册内部类和内部对象的封装类,而是直接以接口方式任意定义组合希望访问的内部方法,由代理系统在后台自动完成类型封装和接口转换的工作。
这一魔术般效果的幕后英雄就是 IRemotingTypeInfo 接口。
实现了 IRemotingTypeInfo 接口的真实代理,其创建的透明代理会在进行类型转换时,将转换请求自动转发给 IRemotingTypeInfo.CanCastTo 方法,并根据返回值来模拟真实的转换效果。
实现上,我们只需要让 ClassProxy 和 ObjectProxy 实现 IRemotingTypeInfo 接口,在其中进行是否能够转换的判断即可,最终方法调用会根据名称和参数,在每方法调用一级重新定位。
如 ObjectProxy 在其 CanCastTo 方法中,首先判断被代理的对象是否直接实现了需转换的接口,如已经实现则直接可以允许转换,具体 Invoke 时自然能够定位到;如果被代理的对象没有实现需转换的接口,则有可能此接口可能是使用者自定义的用于访问内部方法的接口,如前面所定义的 IWindowsIdentityObject 接口。对此类自定义接口,ObjectProxy 不在对象一级进行判断,而是转交给全局唯一用于封装此内部类型的 ClassProxy 处理。
ClassProxy 则在实现 IRemotingTypeInfo.CanCastTo 方法时,直接进行自定义接口的判断,因为在类这个层面,无需支持预定义接口的转换,使用者如果需要可以直接通过 InternalClass.WrappedType 方法获得被包装类型。
因此所有的自定义接口转换判断,实际上都最终落在 ClassProxy.CanCastTo 方法上。此方法将根据需转换接口的所有方法是否都在被封装类型中被实现,来判断是否允许转换到此自定义接口。而通过维护每个内部类型唯一的全局缓存,可以减少这种耗时的冗余检查操作。
可以看到 ClassProxy 维护了内部类型唯一的 _clsIntfs 和 _objIntfs 两级缓存,分别用于缓存访问类静态方法和普通实例方法的自定义接口,而且凡是进行过转换判断的就缓存起来,以便再次转换时节省工作量。进行检测时,首先会检查缓存中是否保存了对此类型进行转换的信息,如果有则直接返回;否则会对需转换类型的每个方法,检查是否在被代理类型中存在;如果有任意一个方法不存在,则转换会失败;然后此转换的判断结果被保存到相应的缓存中,用于下次转换;最终转换结果被返回给 CLR。
具体转换调用堆栈如下:
而通过对 InternalClass 和 InternalObject 的扩展,可以提供显式的类型转换判断机制:
至此,自动类型转换与封装的原理与实现就大概清晰了,通过此模式,可以大大简化对内部类型访问前的薄记工作。
btw: 具体使用的单元测试代码如下:
to be continue...
在上一节中曾经提到,因为 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...