当一个操作数是接口时,回退到 Object 的 == 运算符的原因是什么?



考虑以下类型:

class A { }
class B { }
interface IC { }
A a = null; // the value doesn't matter - null or anything else, for all three
B b = null;
IC c = null;

以下内容不会编译:

var x = a == b;

但是下面的确实编译了(正如我惊讶地发现的那样):

var x = a == c;

据我所知,编译器转而使用默认的==运算符,该运算符是在对象上定义的,因此可以接受任何类型的参数。IL如下(忽略ldfld的详细信息):

ldarg.0
ldfld class A a
ldarg.0
ldfld class IC c
ceq
stloc.0

换句话说,它使用引用相等。

我的问题:

  1. 就语言设计而言,为什么这有意义?对我来说不是,我认为这是一个很大的陷阱。

  2. 如果这确实是一个陷阱,代码分析难道不应该警告我们吗?(没有——没有)。顺便说一句,ReSharper似乎有这个功能。

第二行编译的原因是可能有另一个类从A派生并实现IC。

public class D : A, IC {}
...
a = new D(); c = a; var t = a == c; //t = true;

类只能从一个类继承,因此永远不能创建同时从a和B继承的类,除非a是B的后代,反之亦然。

a == b测试不编译的原因是编译器有足够的信息知道测试不可能是true,因为这两个类不在同一层次结构中。因此,这相当于编译器不允许您错误地编写实际上是常量的条件。

对于接口比较,编译器发现有一个operator==(object, object)可以使用,并且由于AIC都可以隐式转换为object,这就是事实上必须发生的事情。很可能有另一种类型(甚至不一定是引用类型)实现IC,因此条件是真实的。

我很感激R#警告;正如页面所说,在某些情况下,接口类型之间的相等性比较有点可疑,并且有一个非常描述性的、易于替换的解决方案:object.ReferenceEquals。当然,还有一个相反的论点,即相等运算符可能已经过载,因此测试可能是有意义的。或者,它可以是一个简单的人写一个更简洁的风格。显然,尽管在编译器中禁止这样的使用有些过于严厉。

相关内容

最新更新