协方差和上转换的区别

本文关键字:区别 转换 方差 | 更新日期: 2023-09-27 18:02:43

协方差和向上转换之间的区别是什么,或者更具体地说,为什么它们被赋予不同的名称?

我看到过下面的例子被称为'upcasting':

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;

现在,在我没有受过训练的人看来,协方差似乎与向上转换相同,除了它指的是集合的转换。(关于逆变和向下转换也可以做类似的陈述)。

真的那么简单吗?

协方差和上转换的区别

现在,在我没有受过训练的人看来,协方差似乎与向上转换相同,除了它指的是集合的转换。(关于逆变和向下转换也可以做类似的陈述)。

真的那么简单吗?

协方差不是关于向上转换,尽管我可以理解为什么你认为它是相关的。

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

baseSequence = derivedSequence;

注意,这不是向上转换。IEnumerable<Derived>不是由IEnumerable<Base>衍生而来。相反,是协方差允许您将变量derivedSequence的值赋给变量baseSequence。其思想是,Base类型的变量可以从Derived类型的对象赋值,并且由于IEnumerable<T>是协变的参数,因此IEnumerable<Derived>类型的对象可以赋值给IEnumerable<Base>类型的变量。

当然,我还没有真正解释协方差是什么。总的来说,协方差就是下面这个简单的概念。假设你有一个从类型到类型的映射F(我将用F<T>表示这个映射;给定类型T,其映射F下的图像为F<T>。假设这个映射具有以下非常特殊的属性:

如果X是与Y兼容的赋值,那么F<X>也是与F<Y>兼容的赋值。

在这种情况下,我们说F在其参数T中是协变的。(在这里,要说的是"AB "其中AB是引用类型,意味着B的实例可以存储在A类型的变量中。

在我们的例子中,c# 4.0中的IEnumerable<T>,如果Derived是从Base派生的,则从IEnumerable<Derived>实例到IEnumerable<Base>的隐式引用转换。赋值兼容的方向被保留了下来,这就是为什么我们说IEnumerable<T>在其类型形参中是协变的。

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

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

IEnumerable<string>不是从IEnumerable<object>派生的,因此它们之间的转换不是上转换。IEnumerable的类型形参是协变的,string 派生自object,所以允许强制类型转换。

它们是不同概念的原因是,与向上转换不同,协方差并不总是允许的。对于类型系统的设计者来说,将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

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

http://blogs.msdn.com/b/csharpfaq/archive/2010/02/16/covariance-and-contravariance-faq.aspx

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