C#编译器错误?为什么这个隐式的用户定义转换没有编译?

时间:2009-07-30 19:29:01

标签: c# compiler-construction

给出以下结构:

public struct Foo<T>
{
   public Foo(T obj) { }

   public static implicit operator Foo<T>(T input)
   {
      return new Foo<T>(input);
   }
}

此代码编译:

private Foo<ICloneable> MakeFoo()
{
    string c = "hello";
    return c; // Success: string is ICloneable, ICloneable implicitly converted to Foo<ICloneable>
}

但是这段代码没有编译 - 为什么?

private Foo<ICloneable> MakeFoo()
{
    ICloneable c = "hello";
    return c; // Error: ICloneable can't be converted to Foo<ICloneable>. WTH?
}

2 个答案:

答案 0 :(得分:30)

显然,当其中一种类型是接口时,隐式用户定义的转换不起作用。来自C#规范:


6.4.1允许的用户定义转换

C#仅允许声明某些用户定义的转换。特别是,无法重新定义已存在的隐式或显式转换。 对于给定的源类型S和目标类型T,如果S或T是可空类型,则让S0和T0引用它们的基础类型,否则S0和T0分别等于S和T.只有满足以下所有条件时,才允许类或结构声明从源类型S到目标类型T的转换:

  • S0和T0是不同的类型。
  • S0或T0是发生运算符声明的类或结构类型。
  • S0和T0都不是接口类型
  • 不包括用户定义的转化,从S到T或从T到S不存在转换。

在第一种方法中,两种类型都不是接口类型,因此用户定义的隐式转换可以正常工作。

规范不是很清楚,但在我看来,如果涉及的类型之一是接口类型,编译器甚至不会尝试查找任何用户定义的隐式转换。

答案 1 :(得分: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;

现在,是否会调用用户定义的显式转换?该对象实际上是从Foo派生的,所以你希望它不会;这应该是一个简单的类型测试和引用赋值,而不是对辅助方法的调用。 接口值的强制转换始终被视为类型测试,因为对象几乎总是可能属于该类型,并且确实实现了该接口。我们不想否认你做一个廉价的代表保留转换的可能性。

相关问题