10

考虑以下类型:

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;

据我了解,编译器回退到使用默认的 == 运算符,该运算符是在 object 上定义的,因此可以接受任何类型的参数。IL 看起来像这样(忽略 的细节ldfld):

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

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

我的问题:

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

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

4

2 回答 2

7

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

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

类只能从一个类继承,所以你永远不能创建一个从 A 和 B 都继承的类,除非 A 是 B 的后代,反之亦然。

于 2013-02-04T23:01:45.037 回答
4

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

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

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

于 2013-02-04T23:06:44.080 回答