【问题标题】:Any way to implement 'is'/'as' for dynamic or at least fake it?有什么方法可以为动态或至少伪造它实现'is'/'as'?
【发布时间】:2011-08-03 09:50:41
【问题描述】:

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

当然,这种类型的实例可以看作是本机类型 - 类似于 XML 元素 - 但根据 XML 的内容和内容,它也可以看作任何其他对象类型的实例它构建的对象类型(它是由小老我构建的专有 IOC)。

所以

<value>10</value>

可以被视为ConfigValue,但也可能被视为stringshortdouble 等。转换是通过调用语言指定的隐式或显式转换实现的。 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之上的东西;但是必须先查询,然后再查询类型测试;这只会使代码进一步爆炸!

【问题讨论】:

  • 我尝试在DynamicObjectTryBinaryOperation 方法中检查ExpressionType.TypeIs。不走运 - C# 编译器总是发出 IL。我认为最好的办法是提供Is(Type)Is&lt;T&gt;() 方法。虽然请记住,演员阵容仍然可以解决问题 (TryConvertTo),因此您可以只使用演员阵容而不是 ass。
  • @Jonathan - 最后我用静态方法完成了它,而不是扩展动态对象/提供者。我想知道是否有将这种能力添加到 DLR 的案例……可能不会!
  • 我真的认为你应该在connect.microsoft.com 上打开它——这是一个缺点(考虑到ExpressionType 枚举具有TypeIs 值)。
  • @Jonathan - 你是对的 - 到目前为止我的命中率是 50/50,所以我可以这样做!
  • 你能展示你对 BindConvert 的覆盖是如何工作的吗?谢谢。

标签: c# .net dynamic


【解决方案1】:

我认为这是不可能的。

以这段代码为例:

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

【讨论】:

  • 是的 - 我已经反编译并注意到 as/is 不会被转换为动态操作;正如我在问题中所说,这可能是因为 DynamicMetaObject 没有为其提供动态绑定
【解决方案2】:

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

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

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

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

【讨论】:

  • 关于他们不测试转换这一事实的好点 - 这意味着动态绑定它实际上是无稽之谈
【解决方案3】:

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

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

这是最重要的代码 ;)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-12-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多