有什么方法可以为动态实现"is"/"as"或至少伪造它?



我有一个类型ConfigValue,它通过IDynamicMetaObjectProvider和自定义DynamicMetaObject实例的实现公开一个动态接口。

当然,这种类型的实例可以被看作是原生类型——类似于XML Element——但它也可以被看作是任何其他对象类型的实例,这取决于XML的内容和它构建的对象类型(它是由小老头我构建的专有IOC)。

<value>10</value>

可以看作是ConfigValue,但也可能是string, short, double等。根据调用语言的指定,通过隐式或显式强制转换实现转换。XML变得更加复杂,因为你可以调用构造函数,方法,属性获取等等。

当然,为了实现这一点,我已经覆盖了DynamicMetaObject类型的BindConvert成员—如果ConfigValue对象在运行时不支持转换,则会发生运行时错误(这很好)。

我刚刚开始写一段代码,如果我能做一个安全的转换到目标类型,那将是伟大的,但如果这不起作用,退回到其他一些逻辑-类似于:

public DesiredType Foo(dynamic d)
{
  DesiredType dt = d as dt;
  if(dt != null)
    return dt;
  //TODO: Fallback logic to build dt from d
}

但是,c#至少(可能是所有动态感知语言,我敢肯定)不发出任何动态绑定的'as'或'is'操作;大概是因为DynamicMetaObject没有这样的测试方法。因此,类型测试只是在静态类型信息上执行,在这种情况下,总是失败。

因此,我不得不依赖于相当丑陋的:

public DesiredType Foo(dynamic d)
 {
   try
   {
      return (DesiredType)d;
   }
   catch(Exception)
   {
     //TODO: fallback logic
   }
 }

有什么办法可以避免try/catch/gulp模式吗?我能想到的最好的是DynamicMetaObject之上的东西;但是首先要对它进行查询,然后再进行类型测试;这只会使代码进一步爆炸!

我认为这不可能。

以下面的代码为例:

class Program
{
    static void Main(string[] args)
    {
        dynamic d = new object();
        var x = (Program)d;
        Console.WriteLine(x);
        var y = d as Program;
        Console.WriteLine(y);
        var z = d is Program;
        Console.WriteLine(z);
    }
}

如果我们使用Reflector反编译它,我们会发现强制类型转换能够被动态类型拦截的唯一原因是c#编译器为了支持它做了大量额外的工作:

class Program
{
    private static void Main(string[] args)
    {
        object d = new object();
        if (<Main>o__SiteContainer0.<>p__Site1 == null)
        {
            <Main>o__SiteContainer0.<>p__Site1 = CallSite<Func<CallSite, object, Program>>.Create(Binder.Convert(CSharpBinderFlags.ConvertExplicit, typeof(Program), typeof(Program)));
        }
        Console.WriteLine(<Main>o__SiteContainer0.<>p__Site1.Target(<Main>o__SiteContainer0.<>p__Site1, d));
        Program y = d as Program;
        Console.WriteLine(y);
        bool z = d is Program;
        Console.WriteLine(z);
    }
}

相比之下,asis调用被简单地编译为IL指令,没有c#编译器添加的魔法。

这也适用于普通的强制转换操作符;使用as而不是强制转换不会执行任何强制转换,因此永远不会改变底层对象的类型。

isas运行时测试,只有工作于继承,因此它们不需要动态绑定,因为它们已经是动态的。即使没有动态关键字,您也永远不能使用isas来测试隐式或显式转换,而且它们也永远不会对shortdouble这样的值类型起作用。

所以你的答案是没有必要伪造它,它们对动态类型和静态类型的完全相同。您的try catch可能是测试转换的最佳方式,捕获绑定错误是DLR已经在后台做了很多它的回退情况。您可以在调试器中亲自查看是否在第一次出现异常时停止。

改进try catch的最好方法是指定确切的异常。

 catch(RuntimeBinderException)
   {
     //TODO: fallback logic
   }

由于不支持,我编写了这个非常基本的静态方法来简化测试转换的操作。

public static class DynamicHelper
{
    public static TResult As<TResult>(dynamic obj) where TResult : class
    {
        if (obj == null)
            return null;
        try
        {
            return (TResult)obj;
        }
        catch (Exception)
        {
            return null;
        }
    }
}

这是顶级代码;)

最新更新