4

使用下面的代码,我试图让 BindConverter 的覆盖正确,以允许转换到接口 T

class DynamicProxy<T> : DynamicObject
{
    private T t;
    public DynamicProxy(T t)
    {
        this.t = t;
    }
    public override DynamicMetaObject GetMetaObject(Expression parameter)
    {
        return new DynamicProxyMetaObject(parameter, this);
    }
    class DynamicProxyMetaObject : DynamicMetaObject
    {
        public DynamicProxyMetaObject(Expression expression, DynamicObject value)
            : base(expression, BindingRestrictions.Empty, (object)value)
        {                
        }
        public override DynamicMetaObject BindConvert(ConvertBinder binder)
        {
            return base.BindConvert(binder);
        }
    }
}
4

1 回答 1

5

这是 BindConvert 的工作实现:

public override DynamicMetaObject BindConvert(ConvertBinder binder)
{
    BindingRestrictions restrictions 
        = BindingRestrictions.GetTypeRestriction(Expression, LimitType);
    //if the type requested is compatible with the 
    //instance, there's no conversion to be done.
    if (binder.Type.IsAssignableFrom(LimitType))
        return binder.FallbackConvert(
            new DynamicMetaObject(Expression, restrictions, Value));

    if (LimitType.IsGenericType && 
        LimitType.GetGenericTypeDefinition().Equals(typeof(DynamicProxy<>)))
    {
        //get the type parameter for T
        Type proxiedType = LimitType.GetGenericArguments()[0];
        //now check that the proxied type is compatible 
        //with the desired conversion type
        if(binder.ReturnType.IsAssignableFrom(proxiedType))
        {
            //this FieldInfo lookup can be cached by 
            //proxiedType in a static ConcurrentDictionary
            //to cache the reflection for future use
            FieldInfo tField = LimitType.GetField("t", 
                BindingFlags.Instance | BindingFlags.NonPublic);
            //return a field expression that retrieves the 
            //private 't' field of the DynamicProxy
            //note that we also have to convert 'Expression'
            //to the proxy type - which we've already ascertained
            //is the LimitType for this dynamic operation.
            var fieldExpr = Expression.Field(
                Expression.Convert(Expression, LimitType), tField);
            //but because we're allowing bases or interfaces of 'T',
            //it's a good idea to chuck in a 'Convert'
            return new DynamicMetaObject(
                Expression.Convert(fieldExpr, binder.ReturnType),
                restrictions);
        }
    }

    return base.BindConvert(binder);
}

这是一个测试:

[TestMethod]
public void TestConvert()
{
    List<string> myList = new List<string>() { "Hello", "World" };
    //proxy a List<string>
    DynamicProxy<List<string>> proxy1 =
        new DynamicProxy<List<string>>(myList);

    dynamic proxyDynamic = proxy1;
    //dynamic 'cast' to List<string> (the actual 'T')
    //should return same instance, because the conversion
    //simply gets the private 't' field.
    List<string> fromDynamic1 = (List<string>)proxyDynamic;
    Assert.AreSame(myList, fromDynamic1);
    //dynamic 'cast' to a base or interface of T
    //In this case, IEnumerable<string>
    IEnumerable<string> fromDynamic2 = (IEnumerable<string>)proxyDynamic;
    Assert.AreSame(myList, fromDynamic2);
}

并且测试您所要求的确切内容 - 即T接口在哪里 - 也同样有效:

[TestMethod]
public void TestConvert2()
{
  List<string> myList = new List<string>() { "Hello", "World" };
  DynamicProxy<IEnumerable<string>> proxy =
    new DynamicProxy<IEnumerable<string>>(myList);

  dynamic proxyDynamic = proxy;
  var fromDynamic = (IEnumerable<string>)proxyDynamic;
  Assert.AreSame(myList, fromDynamic);      
}

正如测试所示,您不仅可以动态转换为T.,还可以动态转换为T. 但是,请注意,实现首先会检查目标类型是否是 的基/接口DynamicProxy<T>- 所以动态转换object(尽管我不知道为什么要这样做)实际上会返回代理实例。您可以通过删除if第一行之后的第一条语句来禁用该行为BindConvert

LimitType方法中的使用BindConvert至关重要,因为它为您提供了隐藏在动态表达式后面的对象的运行时类型。元对象的Expression属性通常只有一个类型Object- 这不利于钓鱼到对象和调用方法或读取属性/字段,这是我们需要做的以支持动态转换。

因此,使用LimitType使我们能够查看实际DynamicProxy<T>实例内部,既T可以获取实例字段,也可以访问实例字段t(它是私有的,但表达式编译器可以处理)。在确认所需的转换目标类型与 的 兼容TDynamicProxy<T>,我们发出一个表达式,该表达式读取该字段并返回对象作为转换结果。

顺便说一句 - 在第二个测试中,我们目前无法动态转换为List<string>,即使我们传入的代理对象是List<string>- 因为这需要对初始类型检查逻辑进行轻微更改,然后对存储在中的实例进行自省't' 字段检查它的实际类型是否与请求的转换类型兼容。我没有以这种方式完成实施,因为我认为您不太可能这样做。

于 2013-03-13T10:17:56.900 回答