以下代码抛出异常.没有为强制转换接口调用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)
因为它看起来应该也使用隐式转换.另一个错误?
我怀疑这是因为在C#(通常可能是.NET)中,您无法创建用户定义的转换到接口类型(就像您无法创建用户定义的转换到/从基础转换/儿童型).因此,每个接口转换都被视为框或引用转换.
这确实是只是一个猜测,但.
编辑:另一方面,我刚刚看了生成的代码:
dynamic d = ...;
IDisposable x = (IDisposable) d;
Run Code Online (Sandbox Code Playgroud)
并且它确实通过生成动态调用Binder.Convert,因此不是C#编译器执行此操作.嗯.
Chris Burrows 在博客文章On Dynamic Objects and DynamicObject中解释了这种行为:
"当调用站点的底层语言为任何操作提供某种绑定时,DynamicObject(..)还有另一个障碍,该绑定会否决可能存在的任何动态绑定.
(..)回想一下,在C#(6.2.4,项目符号3)中,始终存在从大多数类类型到任何接口类型的显式转换,尽管它们可能会失败.(..)
仅仅为了扩展接口转换示例,它特别奇怪,因为如果转换是隐式的(比如尝试分配给本地),那么动态转换就可以了.为什么?因为C#binder会说,"不!没有隐式转换为IEnumerable,"然后DynamicObject实现会让TryConvert做它的事情."