协方差和向上转换之间有什么区别,或者更具体地说,它们为什么被赋予不同的名称?
我已经看到以下示例称为“向上转换”:
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;
现在,对于我未经训练的眼睛,协方差似乎与向上转换相同,除了它指的是集合的铸造。 (关于逆向和向下转换,可以做出类似的陈述)。
真的那么简单吗?
答案 0 :(得分:57)
现在,对于我未经训练的眼睛,协方差似乎与向上转换相同,除了它指的是集合的铸造。 (关于逆向和向下转换,可以做出类似的陈述)。
真的那么简单吗?
协方差不是关于向上转换,虽然我可以看出为什么你认为它是相关的。
协方差是关于以下非常简单的想法。假设您有derivedSequence
类型的变量IEnumerable<Derived>
。假设您有baseSequence
类型的变量IEnumerable<Base>
。这里,Derived
来自Base
。然后,使用协方差,以下是合法分配,并发生隐式引用转换:
baseSequence = derivedSequence;
请注意,这不是向上转播。 IEnumerable<Derived>
不是IEnumerable<Base>
派生的情况。相反,它是协方差,允许您将变量derivedSequence
的值赋给变量baseSequence
。我们的想法是,Base
类型的变量可以从Derived
类型的对象中分配,并且由于IEnumerable<T>
在其参数中是协变的,因此可以将类型为IEnumerable<Derived>
的对象分配给IEnumerable<Base>
类型的变量。
当然,我还没有真正解释协方差是什么。一般来说,协方差是关于以下简单的想法。假设您有从类型到类型的映射F
(我将F<T>
表示此映射;给定类型T
,映射F
下的映像为{{} 1}}。)让我们说这个映射具有以下非常特殊的属性:
如果
F<T>
的分配与X
兼容,则Y
也会与F<X>
兼容。
在这种情况下,我们说F<Y>
在参数F
中是协变的。 (这里,要说“T
与[{1}}兼容,”其中A
和B
是引用类型,这意味着A
的实例可以存储在B
类型的变量。)
在我们的案例中,C#4.0中的B
,如果A
派生自IEnumerable<T>
,则从IEnumerable<Derived>
到IEnumerable<Base>
的实例进行隐式引用转换。保留了赋值兼容性的方向,这就是为什么我们说Derived
在其类型参数中是协变的。
答案 1 :(得分:19)
投射是指更改对象和表达式的静态类型。
方差是指在某些情况下(例如参数,泛型和返回类型)的类型的可互换性或等价性。
答案 2 :(得分:15)
IEnumerable<string>
不是来自IEnumerable<object>
,因此它们之间的演员阵容不是向上转播。 IEnumerable在其类型参数中是协变的,而字符串是从对象派生的,因此允许强制转换。
答案 3 :(得分:7)
它们是不同概念的原因在于,与向上转换不同,并非总是允许协方差。类型系统的设计者很容易将IList<Cat>
视为来自IList<Animal>
的“派生”,但后来我们遇到了问题:
IList<Cat> cats = new List<Cat>();
IList<Animal> animals = cats;
animals.Add(new Dog()); //Uh oh!
如果允许,现在我们的cats
列表将包含Dog
!
相反,IEnumerable<T>
接口无法添加元素,因此这完全有效(在C#4.0中):
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
答案 4 :(得分:1)
以下博客文章对此有一个很好的解释:
http://blogs.msdn.com/b/csharpfaq/archive/2010/02/16/covariance-and-contravariance-faq.aspx
答案 5 :(得分:0)
从我可以收集的内容中,协方差消除了在之前的上传之后显式向下转换的需要。通常,如果您向上转换对象,则只能访问基本类型方法和属性,使用协方差,您可以通过在更多派生类声明中使用更多派生类型替换较小的派生类型来暗示向下转换。