代码之家  ›  专栏  ›  技术社区  ›  scobi

为什么不相关的c#接口引用可以在没有编译器错误的情况下进行比较?

  •  13
  • scobi  · 技术社区  · 14 年前

    最近,我惊讶地发现编译器对比较接口引用显然并不严格,我想知道为什么它会这样工作。

    class Program
    {
        interface I1 {}
        interface I2 {}
        class C1 : I1 {}
        class C2 : I2 {}
    
        static void Main(string[] args)
        {
            C1 c1 = new C1();
            C2 c2 = new C2();
    
            I1 i1 = c1;
            I2 i2 = c2;
    
            bool x = c1 == c2;
            bool y = i1 == i2;
        }
    }
    

    编译器说我无法比较 c1 == c2 ,如下所示。这些类型完全不相关。不过,我可以比较一下 i1 == i2 . 我希望它会在这里出错,并出现编译时错误,但我很惊讶地发现,您可以将任何接口与任何其他接口进行比较,编译器永远不会抱怨。例如,我可以比较 (I1)null == (IDisposable)null 没问题。

    == 将导致直接引用比较或调用具体类的虚拟Equals。

    我错过了什么?

    4 回复  |  直到 14 年前
        1
  •  12
  •   Eric Lippert    14 年前

    首先,注意Hans引用的是规范的正确部分,但是他引用的规范版本有一个与您的问题相关的拼写错误。修正后的C#4规范指出:

    预定义的引用类型相等 以下内容:

    (1) 两个操作数都是一个值 属于已知为引用类型的类型 或者文字为空。此外,一个 明确的 参考转换 存在于任一操作数的类型中 另一个操作数的类型。

    (2) 一个操作数是T类型的值 其中T是类型参数,而 而且T没有这个值 类型约束。

    除非其中一个 条件是真的,一个约束时间

    这解释了你的观察。任何两个接口之间都有显式的引用转换,因为两个不同接口的任意两个实例 能够

        2
  •  13
  •   Andrew Bezzub    14 年前

    我想这是以这样的方式完成的,因为您可以让一个类型同时继承两个接口,在这种情况下,这样的比较可能很有用:

    interface I1 {}
    interface I2 {}
    class C1 : I1, I2 {}
    

    所以在第一种情况下,编译器肯定知道对象是不同的,但在第二种情况下,它们可能不是。

        3
  •  4
  •   Hans Passant    14 年前

    在C语言规范第7.9.6章“引用类型相等运算符”中有很好的描述:

    预定义的引用类型相等 操作员是:

    bool运算符==(对象x,对象y);
    目标y);

    操作员返回 为了平等或不平等。

    自从 预定义的引用类型相等 对象,它们应用于 不声明适用的运算符== 任何适用的用户定义的等式 操作员有效地隐藏 操作员。

    预定义的引用 以下内容:
    两个操作数都是 引用类型值或文本 转换(§6.3.1)存在于 类型的任一操作数的类型 另一个操作数。
    类型T的值,其中T是 类型参数和其他操作数 没有值类型

    除非其中一个 条件是真的,编译时 这些规则是:
    它是一个 使用 预定义的引用类型相等 比较两个引用的运算符 这是众所周知的不同之处 编译时。例如,如果 操作数的编译时类型是 两种A和B类,如果 A和B都不是从 引用同一对象的两个操作数 反对。因此,手术是 被认为是编译时错误。

    最后一段就是为什么你会犯错误。

        4
  •  0
  •   jordanbtucker    14 年前

    这是真的,但是编译器不知道。这将在运行时确定。