我刚刚意识到我编写了一些我希望无效的代码(关于语法),但编译器接受了。
为了简洁起见,我重新创造了一个例子。给出一个类型:
private class MyType
{
}
然后,我可以将MyType
的实例与IEnumerable<MyType>
进行比较:
var myCollection = new List<MyType> { };
var test = new MyType() == (IEnumerable<MyType>)myCollection;
我很惊讶我能比较两种不同类型的物体。更有趣(至少对我有趣),如果我将演员表移除到IEnumerable
我无法比较它。为什么会这样? List
实现了IEnumerable
,所以假设某些平等比较器在某处我错过了(可能是错误的),为什么List
也没有这个呢?
答案 0 :(得分:7)
编译器允许您将对象与接口进行比较,但它不允许您将它们与不相关的具体类进行比较,因为它预先知道精确类型并且知道无法比较这两种类型。允许与接口进行比较,因为将在运行时尝试来自具体对象的强制转换,这本身就是一个有效的操作。
你究竟打算做什么是一个不同的故事,事实上你的所有比较都不会在语义上正确。
在任何情况下,您的代码都不会产生真正的比较。它会在运行时返回False
。
var test1 = new MyType() == (IEnumerable<MyType>)myCollection; // False
var test2 = new MyType() == new List<MyType>(); // Compile-time error
var test3 = new MyType() == (IComparable)5; // False
另一个问题是使用==
- 这在另一个层面上是错误的。您也可以自由地调用Equals
,并且似乎尝试执行真正的语义上有意义的比较。默认实现不会做任何有意义的事情,但是再次 - 从编译器的角度来看,一切都在语法上是正确的。
答案 1 :(得分:3)
以下是关于该规范的引用(第7.10.6节引用类型相等运算符)。本节介绍在这种情况下使用的预定义对象相等运算符(== (object x, object y)
):
预定义的引用类型相等运算符需要其中一个 以下:
•两个操作数都是已知类型的值 reference-type或文字null。此外,一个明确的参考 转换(第6.2.4节)从操作数的类型到类型存在 另一个操作数。
•一个操作数是T类型的值,其中T是a type-parameter和另一个操作数是文字null。此外 T没有值类型约束。
从第6.2.4节我们可以发现:
显式引用转换为:
•从任何类型S到任何接口类型T,只要S不是 密封并提供S不实施T.
因为那个
var test = new MyType() == (IEnumerable<MyType>)myCollection;
有效,因为如上所述,从MyType
到IEnumerable<MyType>
的显式引用转换。但是,如果你使MyType
密封 - 那么将没有这样的转换,并且不会编译。如果你使用一些预定义的密封类型会发生同样的情况,例如string
(我的意思是 - 它不会编译)。
允许将非密封类型显式引用转换为任何接口的原因是因为该变量的运行时类型可以是任何子类,并且该子类可能实现该接口。密封类不能被继承,因此编译器可以确定不是这种情况。