C# 协方差和向上投影之间的差异

C# 协方差和向上投影之间的差异,c#,casting,covariance,C#,Casting,Covariance,协方差和向上投射之间有什么区别,或者更具体地说,为什么给它们起不同的名字 我看到了以下被称为“向上投射”的示例: string s = "hello"; object o = s; //upcast to 'string' to 'object' 然而,我看到的以下内容被称为“协方差”: string[] s = new string[100]; object[] o = s; IEnumerable<string> ies = new List<string>()

协方差和向上投射之间有什么区别,或者更具体地说,为什么给它们起不同的名字

我看到了以下被称为“向上投射”的示例:

string s = "hello";
object o = s;  //upcast to 'string' to 'object'
然而,我看到的以下内容被称为“协方差”:

string[] s = new string[100];
object[] o = s;

IEnumerable<string> ies = new List<string>();
IEnumerable<object> ieo = ies;
string[]s=新字符串[100];
对象[]o=s;
IEnumerable ies=新列表();
IEnumerable ieo=ies;
现在,在我未经训练的眼中,协方差似乎与向上投射相同,只是它指的是集合的投射。(关于对冲和下调,也可以做出类似的声明)


它真的那么简单吗?

IEnumerable
不是从
IEnumerable
派生出来的,因此它们之间的转换不是向上转换。IEnumerable的类型参数是协变的,字符串是从object派生的,因此允许强制转换。

强制转换指更改对象和表达式的静态类型


差异是指类型在某些情况下(如参数、泛型和返回类型)的可互换性或等价性。

下面的博客文章对此有很好的解释:

现在,在我未经训练的眼中,协方差似乎与向上投射相同,只是它指的是集合的投射。(关于对冲和下调,也可以做出类似的声明)

真的那么简单吗

协方差与向上投射无关,尽管我可以理解为什么你认为它是相关的

协方差是关于以下非常简单的想法。假设您有一个类型为
IEnumerable
的变量
derivedSequence
。假设您有一个类型为
IEnumerable
的变量
baseSequence
。这里,
Derived
派生自
Base
。然后,使用协方差,以下为合法赋值,并发生隐式引用转换:

baseSequence = derivedSequence;
请注意,这不是向上投射。
IEnumerable
并非源于
IEnumerable
。相反,协方差允许您将变量
derivedSequence
的值分配给变量
baseSequence
。其思想是
Base
类型的变量可以从
Derived
类型的对象分配,并且由于
IEnumerable
在其参数中是协变的,因此
IEnumerable
类型的对象可以分配给
IEnumerable
类型的变量

当然,我还没有真正解释什么是协方差。一般来说,协方差是关于以下简单的想法。假设您有一个从类型到类型的映射
F
(我将用
F
表示这个映射;给定一个类型
T
它在映射
F
下的图像是
F
),假设这个映射具有以下非常特殊的属性:

如果
X
的赋值与
Y
兼容,则
F
的赋值也与
F
兼容

在这种情况下,我们说
F
在其参数
T
中是协变的。(这里,说“
A
是与
B
兼容的赋值,
A
B
是引用类型意味着
B
的实例可以存储在
A
类型的变量中)


在我们的例子中,
IEnumerable
在C#4.0中,从
IEnumerable
的实例到
IEnumerable
的隐式引用转换,如果
Derived
是从
Base
派生的。分配兼容性的方向被保留,这就是为什么我们说,
IEnumerable
在其类型参数中是协变的。

它们是不同概念的原因是,与向上转换不同,协变并不总是被允许的。类型系统的设计者很容易将
IList
视为从
IList
中“派生”而来,但随后我们遇到了问题:

IList<Cat> cats = new List<Cat>();
IList<Animal> animals = cats; 
animals.Add(new Dog()); //Uh oh!

从我所能收集到的数据来看,协方差消除了在上一次向上投射之后显式向下投射的需要。通常,如果你向上转换一个对象,你只能访问基类型的方法和属性,通过协方差,你似乎可以通过在更派生的类声明中用更多的派生类型替换更少的派生类型来暗示向下转换。

因为这是一个非常流行的问题,我本打算进一步阐述这个答案,但我会参考@Jason的非常好的答案。到目前为止,这是唯一一个真正解释差异的答案,做得很好。是的,答案很好,至少应该得到与当前问题一样多的选票。因此,我们接受我现在将要表演的观点……当然,这绝不会诋毁其他答案:-)杰森的答案非常正确。有关协方差和赋值兼容性之间差异的更详细解释,请参阅我的文章。你并不孤单;很多人把这两个人搞糊涂了。@Eric Lippert:干杯。如果我以前读过那篇文章,我可能永远不会发布这篇文章!很高兴我这么做了。事实上,我很惊讶现在还没有类似的问题。
IList<Cat> cats = new List<Cat>();
IEnumerable<Animal> animals = cats;
//There's no way to add things to an IEnumerable<Animal>, so here we are ok