任何方式实现'是'/'作为'动态或至少是假的吗?

时间:2011-08-03 09:50:41

标签: c# .net dynamic

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

当然,可以看到这种类型的实例,因为它是本机类型 - 类似于XML元素 - 但它也可以被视为任何其他对象类型的实例,根据XML的内容和什么它构建的一种对象(它是一个由老我建立的专有IOC)。

所以

<value>10</value>

可以看作是ConfigValue,也可能是stringshortdouble等。转化是通过隐式或显式转换实现的,如调用所指定的语言。 XML变得更复杂,因为你可以触发构造函数,方法,属性获取等。

为实现这一点,当然,我已经覆盖BindConvert类型的DynamicMetaObject成员 - 如果运行时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之上的东西;但是之后必须首先查询,然后再进行类型测试;这将进一步爆炸代码!

3 个答案:

答案 0 :(得分:2)

我认为不可能。

以此代码为例:

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而不是强制转换将不会执行任何转换转换,因此永远不会更改基础对象的类型。

答案 1 :(得分:1)

isas 运行时 测试仅 继承因此它们不需要动态绑定,因为它们已经是动态的。即使没有动态关键字,您也绝不能使用isas来测试隐含或显式转化,而且它们也不会使用shortdouble之类的值类型。

所以你的答案是没有必要伪造它,它们与静态类型的动态类型完全相同 。您的try catch可能是测试转换的最佳方法,捕获绑定错误是DLR在后台针对很多后备案例已经做的事情。如果您停止第一次机会异常,您可以在调试器中看到自己。

改善try catch的最佳方法是指定确切的例外情况。

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

答案 2 :(得分:0)

代替它不受支持的事实,我已经编写了这个非常基本的静态方法来简化测试转换的操作。

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;
        }
    }
}

这是顶级抽屉代码;)