协方差和向上转换之间的区别
协变和向上转换之间有什么区别,或者更具体地说,为什么它们有不同的名称?
我见过以下被称为“向上转型”的示例:
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;
现在,在我未经训练的眼中,协变似乎与向上转型相同,只是它指的是集合的转型。 (关于逆变和向下转型也可以做出类似的陈述)。
真的有那么简单吗?
What is the difference between covariance and upcasting, or, more specifically, why are they given different names?
I've seen the following example referred to as 'upcasting':
string s = "hello";
object o = s; //upcast to 'string' to 'object'
Whereas, the following I have seen called 'covariance':
string[] s = new string[100];
object[] o = s;
IEnumerable<string> ies = new List<string>();
IEnumerable<object> ieo = ies;
Now, to my untrained eye, covariance seems to be the same as upcasting, except that it refers the casting of collections. (And of a similar statement can be made regarding contravariance and downcasting).
Is it really that simple?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
协方差与向上转换无关,尽管我可以理解为什么你认为它是相关的。
协方差是关于以下非常简单的想法。假设您有一个
IEnumerable
类型的变量衍生序列
。假设您有一个IEnumerable
类型的变量baseSequence
。这里,Derived
派生自Base
。然后,使用协变,以下是合法的赋值,并且会发生隐式引用转换:请注意,这不是向上转换。
IEnumerable
并非从IEnumerable
派生。相反,协方差允许您将变量衍生序列
的值分配给变量baseSequence
。这个想法是,Base
类型的变量可以从Derived
类型的对象进行赋值,并且由于IEnumerable
在其参数中是协变的,IEnumerable
类型的对象可以分配给IEnumerable
类型的变量。当然,我还没有真正解释什么是协方差。一般来说,协方差是关于以下简单的想法。假设您有一个从类型到类型的映射
F
(我将用F
表示此映射;给定类型T
其映射F
下的图像是F
。)假设此映射具有以下非常特殊的属性:在这种情况下,我们说
F
的参数T
是协变的。 (这里,“A
与B
的赋值兼容”,其中A
和B
是引用类型,意味着B
的实例可以存储在A
类型的变量中。)在我们的例子中,C# 4.0 中的在其类型参数中是协变的。
IEnumerable
是一个隐式引用从实例转换如果Derived
是从Base
派生的,则将IEnumerable
转换为IEnumerable
。赋值兼容性的方向被保留,这就是为什么我们说 IEnumerableCovariance isn't about upcasting, although I can see why you think it's related.
Covariance is about the following very simple idea. Let's say you have a variable
derivedSequence
of typeIEnumerable<Derived>
. Let's say you have a variablebaseSequence
of typeIEnumerable<Base>
. Here,Derived
derives fromBase
. Then, with covariance, the following is a legal assignment, and an implicit reference conversion occurs:Note that this is not upcasting. It is not the case that
IEnumerable<Derived>
derives fromIEnumerable<Base>
. Rather, it is covariance that allows you to assign the value of the variablederivedSequence
to the variablebaseSequence
. The idea is that variables of typeBase
can be assigned from objects of typeDerived
, and sinceIEnumerable<T>
is covariant in its parameter, objects of typeIEnumerable<Derived>
can be assigned to variables of typeIEnumerable<Base>
.Of course, I haven't yet really explained what covariance is. In general, covariance is about the following simple idea. Let's say you have a mapping
F
from types to types (I'll denote this mapping byF<T>
; given a typeT
its image under the mappingF
isF<T>
.) Let's say that this mapping has the following very special property:In this case, we say that
F
is covariant in its parameterT
. (Here, to say that "A
is assignment compatible withB
" whereA
andB
are reference types means that instances ofB
can be stored in variables of typeA
.)In our case,
IEnumerable<T>
in C# 4.0, an implicit reference conversion from instances ofIEnumerable<Derived>
toIEnumerable<Base>
ifDerived
is derived fromBase
. The direction of assignment compatibility is preserved, and this is why we say thatIEnumerable<T>
is covariant in its type parameter.转换是指改变对象和表达式的静态类型。
方差是指在某些情况下(例如参数、泛型和返回类型)类型的可互换性或等价性。
Casting refers to changing the static type of objects and expressions.
Variance refers to the interchangeability or equivalence of types in certain situations (such as parameters, generics, and return types).
IEnumerable
不是从IEnumerable
IEnumerable<string>
is not derived fromIEnumerable<object>
, so the cast between them is not upcasting. IEnumerable is covariant in its type parameter and string is derived from object, so the cast is allowed.它们是不同概念的原因是,与向上转型不同,协变并不总是被允许的。对于类型系统的设计者来说,将
IList
视为IList
的“派生”是很容易的>,但随后我们遇到了问题:如果允许的话,现在我们的
cats
列表将包含Dog
!相反,
IEnumerable
接口无法添加元素,因此这是完全有效的(在 C# 4.0 中):The reason they are different concepts is that, unlike upcasting, covariance is not always allowed. It would have been easy for the designers of the type-system to make
IList<Cat>
be considered as "derived" fromIList<Animal>
, but then we run into problems:If this were allowed, now our
cats
list would contain aDog
!In contrast, the
IEnumerable<T>
interface has no way of adding elements, so this is perfectly valid (in C# 4.0):下面的博客文章对此有很好的解释:
http://blogs.msdn.com/b/csharpfaq/archive/2010/02/16/covariance-and-contravariance-faq.aspx
Thie blog post below has a good explanation of it:
http://blogs.msdn.com/b/csharpfaq/archive/2010/02/16/covariance-and-contravariance-faq.aspx
据我所知,协方差消除了在先前的向上转型之后进行显式向下转型的需要。通常,如果您向上转换对象,则只能访问基类型方法和属性,通过协变,您似乎可以通过在更多派生类声明中用更多派生类型替换更少派生类型来暗示向下转换。
From what I can gather covariance removes the need for explicit downcasting subsequent to a previous upcast. Typically if you upcast an object you can only access the base type methods and attributes, with covariance it seems you can imply the downcast by replacing lesser derived types with more derived types in the more derived class declaration.