C# 为什么我可以将一个对象与这些对象中的IEnumerable进行比较,而不是列表?

C# 为什么我可以将一个对象与这些对象中的IEnumerable进行比较,而不是列表?,c#,types,casting,ienumerable,iequalitycomparer,C#,Types,Casting,Ienumerable,Iequalitycomparer,我刚刚意识到我编写的代码可能是无效的(就语法而言),但编译器接受 为了简洁起见,我重新创建了一个示例。给定类型: private class MyType { } 然后,我可以将MyType的实例与IEnumerable进行比较: var myCollection=新列表{}; var test=new MyType()==(IEnumerable)myCollection; 我很惊讶我能比较两个不同类型的物体。更有趣的是(至少对我来说是有趣的),如果我删除对IEnumerable的强制转换

我刚刚意识到我编写的代码可能是无效的(就语法而言),但编译器接受

为了简洁起见,我重新创建了一个示例。给定类型:

private class MyType
{
}
然后,我可以将
MyType
的实例与
IEnumerable
进行比较:

var myCollection=新列表{};
var test=new MyType()==(IEnumerable)myCollection;

我很惊讶我能比较两个不同类型的物体。更有趣的是(至少对我来说是有趣的),如果我删除对
IEnumerable
的强制转换,我就无法进行比较。为什么会这样?
List
实现了
IEnumerable
,所以假设我缺少某种类型的等式比较器(可能是错误的),为什么
List
也不能使用它呢?

编译器允许您将对象与接口进行比较,但它不允许您将它们与不相关的具体类进行比较,因为它预先知道确切的类型,并且知道这两种类型无法进行比较。允许与接口进行比较,因为将在运行时尝试从具体对象进行转换,这本身就是一个有效的操作

你到底要用它做什么是另一回事,事实上,你所有的比较在语义上都是不正确的

在任何情况下,您的代码都不会产生真正的比较。运行时,它将返回
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
var test1=new MyType()==(IEnumerable)myCollection;//假的
var test2=new MyType()==new List();//编译时错误
var test3=new MyType()==(IComparable)5;//假的

另一个问题是使用
==
——这在另一个层面上是错误的。您也可以自由地调用
Equals
,似乎可以尝试执行真正的语义上有意义的比较。默认实现不会做任何有意义的事情,但从编译器的角度来看,一切在语法上都是正确的。

这里引用了规范中的相关内容(第7.10.6节引用类型相等运算符)。本节介绍在本例中使用的预定义对象相等运算符(
==(对象x,对象y)
):

预定义的引用类型相等运算符需要 以下:

•两个操作数都是已知类型的值 引用类型或文本为null。此外,一个明确的引用 存在从任一操作数类型到类型的转换(§6.2.4) 另一个操作数的

•一个操作数是T类型的值,其中T是 类型参数,另一个操作数为文本null。此外 T没有值类型约束

从第6.2.4节我们可以发现:

显式引用转换为:

•从任何类别类型S到任何接口类型T,前提是S不是 密封和提供的S不执行T

因此

var test = new MyType() == (IEnumerable<MyType>)myCollection;
var test=newmytype()==(IEnumerable)myCollection;
有效,因为如上所述,存在从
MyType
IEnumerable
的显式引用转换。但是,如果将
MyType
密封,则不会进行此类转换,也不会编译。如果使用一些预定义的密封类型,例如
string
(我的意思是-它不会编译),也会发生同样的情况


允许将非密封类型显式引用转换为任何接口的原因是,该变量的运行时类型可以是任何子类,并且该子类可能实现该接口。密封类不能被继承,因此编译器可以确定情况并非如此。

我无法比较它。
告诉我们这是什么意思。@mjwills-语法错误。无论如何,您不会比较对象/列表的(内容),您只会比较引用。并且没有重写的
=
运算符将
MyType
列表
作为参数。这个问题有趣的部分(对我来说)is:使用
MyType
和显式
IEnumerable
调用
==
时,哪个运算符在哪种类型中,使用哪种类型的参数。在编译时编译器没有足够的信息来知道
IEnumerable
或任何其他
接口背后的真正类型。。。但是它知道,
List
不能与
MyType
相比。。。基本上你们并没有向编译器提供足够的信息。你们能为第一段引用一些规范吗?我的意思是,这里是如何进行运算符解析的?这是明确的规定吗?或者在每个
CustomType
中都有一个默认的
==
操作符,第一个参数是
CustomType
,第二个参数是..那么..如何声明“仅接口”?有一个从
列表
对象
的隐式转换…我想我必须再次阅读规范…我没有精确的引用,但在我的头上
==
将变成
引用相等(对象,对象)
除非有一个自定义的
==
操作符匹配左手和右手对象的编译时类型(包括隐式转换)。您有右Zoran(e),@RenéVogt相关规范部分是“7.10.6引用类型相等操作符”,前几段。在这里,您将发现,除非存在从一个操作数到另一个操作数的显式引用转换,否则会发生绑定错误(因此,编译错误)。在“6.2.4”部分中,您可以发现从任何接口到任何非密封类都存在显式引用转换(例如,
var test=(IEnumerable)new MyType()
编译良好,即使该类型没有实现此接口)。@ZoranHorvat-非常感谢您的回答,当它允许我
var test = new MyType() == (IEnumerable<MyType>)myCollection;