Jud*_*ngo 32 c# compiler-construction
给定以下结构:
public struct Foo<T>
{
public Foo(T obj) { }
public static implicit operator Foo<T>(T input)
{
return new Foo<T>(input);
}
}
Run Code Online (Sandbox Code Playgroud)
此代码编译:
private Foo<ICloneable> MakeFoo()
{
string c = "hello";
return c; // Success: string is ICloneable, ICloneable implicitly converted to Foo<ICloneable>
}
Run Code Online (Sandbox Code Playgroud)
但是这段代码没有编译 - 为什么?
private Foo<ICloneable> MakeFoo()
{
ICloneable c = "hello";
return c; // Error: ICloneable can't be converted to Foo<ICloneable>. WTH?
}
Run Code Online (Sandbox Code Playgroud)
Phi*_*ert 30
显然,当其中一个类型是接口时,隐式用户定义的转换不起作用.来自C#规范:
6.4.1允许的用户定义转换
C#仅允许声明某些用户定义的转换.特别是,无法重新定义已存在的隐式或显式转换.对于给定的源类型S和目标类型T,如果S或T是可空类型,则让S0和T0引用它们的基础类型,否则S0和T0分别等于S和T. 仅当满足以下所有条件时,才允许类或结构声明从源类型S到目标类型T的转换:
在第一种方法中,两种类型都不是接口类型,因此用户定义的隐式转换有效.
规范不是很清楚,但在我看来,如果涉及的类型之一是接口类型,编译器甚至不会尝试查找任何用户定义的隐式转换.
Eri*_*ert 24
(跟随接受的答案的评论.)
是的,这是规范的一个非常非常令人困惑的部分.关于"包含类型"的整个过程尤其存在严重缺陷.几年来我一直在努力寻找时间将整个部分完全改写成更连贯的东西,但它从未有过足够高的优先级.
基本上我们在这里得到的是一个矛盾; 我们说没有涉及接口的用户定义的隐式转换,但在这种情况下显然不是这样; 从IC到用户定义的隐式转换Foo<IC>,通过字符串转到Foo<IC>该转换的事实证明了这一点.
我们应该更好地强调的是你引用的这一行:
特别是,无法重新定义已存在的隐式或显式转换.
这就是推动整个事情的动力; 当你实际上调用用户定义的方法时,不希望你曾经认为你正在进行表示保留类型测试.考虑一下这种变化:
interface IBar {}
interface IFoo : IBar {}
class Foo<T> : IFoo
{
public static explicit operator Foo<T>(T input) { whatever }
}
class Blah : Foo<IBar> {}
...
IBar bar = new Blah();
Foo<IBar> foo = (Foo<IBar>)bar;
Run Code Online (Sandbox Code Playgroud)
现在,它是否会调用用户定义的显式转换?该对象真的来自Foo,所以你希望它不会; 这应该是一个简单的类型测试和引用赋值,而不是对辅助方法的调用.接口值上的强制转换始终被视为类型测试,因为对象实际上几乎总是可能属于该类型并且确实实现了该接口. 我们不想否认你做一个廉价的代表保留转换的可能性.