最近我很惊讶发现编译器对比较接口引用显然并不严格,并且想知道它为什么会这样工作。
考虑以下代码:
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
,没问题。
接口不是对象吗?它们是一种特殊的参考类型吗?我的期望是==
会导致直接引用比较或调用具体类的虚拟等于。
我错过了什么?
答案 0 :(得分:13)
我认为这是以这种方式完成的,因为你可以拥有一个继承两个接口的类型,对于这种情况,这种比较可能很有用:
interface I1 {}
interface I2 {}
class C1 : I1, I2 {}
因此,在第一种情况下,编译器肯定知道对象是不同的,但在第二种情况下,它们可能不是。
答案 1 :(得分:12)
首先,请注意Hans正在引用规范的正确部分,但他引用的规范版本中有一个与您的问题相关的拼写错误。纠正的C#4规范说:
预定义的引用类型相等 运营商需要其中一个 以下:
(1)两个操作数都是一个值 已知为参考类型的类型 或文字null。此外,一个 显式引用转换 存在于任一操作数的类型 到另一个操作数的类型。
(2)一个操作数是T类型的值 其中T是一个类型参数和 其他操作数是文字null。 此外,T没有价值 类型约束。
除非其中一个 条件是真的,一个约束时间 发生错误。
这解释了你的观察。任何两个接口之间都有明确的引用转换,因为两个不同接口的任何两个实例都可以引用同一个对象。可能有一个实现I1和I2的C3类,你可以对同一个C3实例进行参考比较,一个转换为I1,另一个转换为I2。
答案 2 :(得分:4)
在C#语言规范第7.9.6章“引用类型相等运算符”中很好地描述了它:
预定义的引用类型相等 运营商是:
bool operator ==(object x,object y);
bool operator!=(对象x, 对象y);运营商返回 比较两个参考的结果 为了平等或不平等。
由于 预定义的引用类型相等 运算符接受类型的操作数 对象,它们适用于所有类型 不要声明适用的运营商== 和operator!=成员。反过来, 任何适用的用户定义的相等 运营商有效地隐藏了 预定义的引用类型相等 运营商。
预定义参考 类型相等运算符需要其中一个 以下内容:
•两个操作数都是 引用类型值或文字 空值。此外,标准隐含 转换(第6.3.1节)存在于 类型的任一操作数的类型 另一个操作数 •一个操作数是a 类型T的值,其中T是a type-parameter和另一个操作数 是文字null。此外T 没有值类型 约束。除非其中一个 条件是真的,编译时 发生错误。值得注意的意义 这些规则是:
• 它是一个 编译时错误使用 预定义的引用类型相等 运营商比较两个参考 众所周知的是不同的 编译时间。例如,如果 编译时类型的操作数是 两个类类型A和B,如果 既不是A也不是B来源于 另外,那就不可能了 两个操作数引用相同 宾语。因此,操作是 被认为是编译时错误。
最后一段是您收到错误的原因。
答案 3 :(得分:0)
我的期望是a ==会导致直接引用比较或调用具体类的虚拟等于。
这是事实,但编译器不知道这一点。这将在运行时确定。