例如.我们总是可以把猫的一个例子视为动物的一个例子.复杂类型< Cat>可以被视为ComplexType< Animal>如果ComplexType是协方差的.
我想知道:协方差的“类型”是什么,它们如何与C#相关(是否支持?)
代码示例将是有帮助的.
例如,一种类型是返回类型协方差,由Java支持,但不是C#.
我希望有功能的编程人员也可以进来,
解决方法
更新
阅读建设性意见和Eric Lippert指出(和写的)大量文章后,我改进了答案:
更新了阵列协方差的破坏
>添加“纯”委托方差
>从BCL增加了更多的例子
>添加了对深入解释概念的文章的链接.
>增加了关于高阶函数参数协方差的全新部分.
返回类型协方差:
C#中不支持Java(> = 5)[1]
和C [2]
(Eric Lippert解释why not和what you can do about it):
class B { B Clone(); } class D: B { D Clone(); }
BCL将通用的IEnumerable接口定义为协变量:
IEnumerable<out T> {...}
因此,以下示例是有效的:
class Animal {} class Cat : Animal {} IEnumerable<Cat> cats = ... IEnumerable<Animal> animals = cats;
请注意,IEnumerable的定义是“只读” – 您不能向其添加元素.
与IList< T>的定义相反,其可以被修改.使用.Add():
public interface IEnumerable<out T> : ... //covariant - notice the 'out' keyword public interface IList<T> : ... //invariant
class Animal {} class Cat : Animal {} class Prog { public delegate Animal AnimalHandler(); public static Animal GetAnimal(){...} public static Cat GetCat(){...} AnimalHandler animalHandler = GetAnimal; AnimalHandler catHandler = GetCat; //covariance }
“纯”代表协方差[5 - pre-variance-release article]
– 支持C#
没有参数并返回某些东西的委托的BCL定义是协变的:
public delegate TResult Func<out TResult>()
这允许以下内容:
Func<Cat> getCat = () => new Cat(); Func<Animal> getAnimal = getCat;
string[] strArray = new[] {"aa","bb"}; object[] objArray = strArray; //covariance: so far,so good //objArray really is an "alias" for strArray (or a pointer,if you wish) //i can haz cat? object cat == new Cat(); //a real cat would object to being... objectified. //now assign it objArray[1] = cat //crash,boom,bang //throws ArrayTypeMismatchException
最后 – 令人惊讶和有点头脑弯曲
委托参数协方差(yes,即协方差) – 用于高阶函数
承担一个参数并且不返回任何事件的委托的BCL定义是相反的:
public delegate void Action<in T>(T obj)
跟我一起让我们来定义一个马戏团的动物训练师 – 他可以被告知如何训练一只动物(通过给他一个与该动物合作的动作).
delegate void Trainer<out T>(Action<T> trainingAction);
我们有训练师的定义,让我们得到一个教练,让他上班.
Trainer<Cat> catTrainer = (catAction) => catAction(new Cat()); Trainer<Animal> animalTrainer = catTrainer; // covariant: Animal > Cat => Trainer<Animal> > Trainer<Cat> //define a default training method Action<Animal> trainAnimal = (animal) => { Console.WriteLine("Training " + animal.GetType().Name + " to ignore you... done!"); }; //work it! animalTrainer(trainAnimal);
输出证明这是有效的:
Training Cat to ignore you… done!
为了理解这个,一个笑话是有序的.
A linguistics professor was lecturing to his class one day.
“In English,” he said,“a double negative forms a positive.
However,” he pointed out,“there is no language wherein a double positive can form a negative.”A voice from the back of the room piped up,“Yeah,right.”
这与协方差有什么关系?
让我尝试一下餐巾纸展示.
动作< T>是逆转的,即它“翻转”类型的关系:
A < B => Action<A> > Action<B> (1)
使用动作< A>更改上述A和B和Action< B>并得到:
Action<A> < Action<B> => Action<Action<A>> > Action<Action<B>> or (flip both relationships) Action<A> > Action<B> => Action<Action<A>> < Action<Action<B>> (2)
把(1)和(2)放在一起,我们有:
,-------------(1)--------------. A < B => Action<A> > Action<B> => Action<Action<A>> < Action<Action<B>> (4) `-------------------------------(2)----------------------------'
但是我们的培训师< T>代表实际上是一个Action< Action< T>:
Trainer<T> == Action<Action<T>> (3)
所以我们可以重写(4)为:
A < B => ... => Trainer<A> < Trainer<B>
– 根据定义,培训师是协调的.
简而言之,应用动作两次,我们得到对抗方差,即类型之间的关系被翻转两次(见(4)),所以我们回到协方差.