在转换为接口类型时未调用DynamicObject.TryConvert

And*_*vey 21 c# dynamic

以下代码抛出异常.没有为强制转换接口调用TryConvert.为什么是这样?我可以解决这个问题吗?

using System.Dynamic;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            dynamic test = new JsonNull();
            var ok = (string)test;
            // Next line throws:
            // Unable to cast object of type 'ConsoleApplication1.JsonNull' to type 'ConsoleApplication1.IFoo'.
            var fail = (IFoo)test;
        }
    }

    class JsonNull : DynamicObject
    {
        public override bool TryConvert(ConvertBinder binder, out object result)
        {
            result = null;
            return !binder.Type.IsValueType;
        }
    }

    interface IFoo { }
}
Run Code Online (Sandbox Code Playgroud)

Jor*_*dão 13

我发现如果你改变这一行:

var fail = (IFoo)test; 
Run Code Online (Sandbox Code Playgroud)

对此:

IFoo success = test;
Run Code Online (Sandbox Code Playgroud)

它按预期工作.

在这种情况下,似乎只有隐式转换才有效.对我来说看起来像个错误.

我也觉得这也很失败:

class Program {
  static void Main(string[] args) {
    dynamic test = new JsonNull();
    Fails(test);
  }
  static void Fails(IFoo ifoo) { }
}
// ...
Run Code Online (Sandbox Code Playgroud)

因为它看起来应该也使用隐式转换.另一个错误?


Jon*_*eet 9

我怀疑这是因为在C#(通常可能是.NET)中,您无法创建用户定义的转换到接口类型(就像您无法创建用户定义的转换到/从基础转换/儿童型).因此,每个接口转换都被视为框或引用转换.

这确实只是一个猜测,但.

编辑:另一方面,我刚刚看了生成的代码:

dynamic d = ...;
IDisposable x = (IDisposable) d;
Run Code Online (Sandbox Code Playgroud)

并且它确实通过生成动态调用Binder.Convert,因此不是C#编译器执行此操作.嗯.


blu*_*ing 7

Chris Burrows 在博客文章On Dynamic Objects and DynamicObject中解释了这种行为:

"当调用站点的底层语言为任何操作提供某种绑定时,DynamicObject(..)还有另一个障碍,该绑定会否决可能存在的任何动态绑定.

(..)回想一下,在C#(6.2.4,项目符号3)中,始终存在从大多数类类型到任何接口类型的显式转换,尽管它们可能会失败.(..)

仅仅为了扩展接口转换示例,它特别奇怪,因为如果转换是隐式的(比如尝试分配给本地),那么动态转换就可以了.为什么?因为C#binder会说,"不!没有隐式转换为IEnumerable,"然后DynamicObject实现会让TryConvert做它的事情."