什么是C#中的各种协方差的? (或者,协方差:通过实施例) [英] What are the kinds of covariance in C#? (Or, covariance: by example)

查看:200
本文介绍了什么是C#中的各种协方差的? (或者,协方差:通过实施例)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

协方差为(大约)的能力的镜继承在复杂类型中使用它们。结果
例如简单类型的
的我们可以始终将的实例作为动物的一个实例。 A 的ComplexType<猫> 可被视为一个的ComplexType<动物> ,如果复杂类型协变的。

Covariance is (roughly) the ability to mirror inheritance of "simple" types in complex types that use them.
E.g. We can always treat an instance of Cat as an instance of Animal. A ComplexType<Cat> may be treated as a ComplexType<Animal>, if ComplexType is covariant.

我想知道:什么是类型协方差,以及他们如何涉及到C#(被他们的支持?)结果
码例子将是有益的。

I'm wondering: what are the "types" of covariance, and how do they relate to C# (are they supported?)
Code examples would be helpful.

例如,一类是的返回类型的协方差的,由Java的支持,但不是C#。

For instance, one type is return type covariance, supported by Java, but not C#.

我希望有人函数式编程印章可以附和呢!

I'm hoping someone with functional programming chops can chime in, too!

推荐答案

这是我能想到的:

更新

阅读建设性的意见后,和吨的文章指出(和书面)由Eric利珀特,我改进了答案:

After reading the constructive comments and the ton of articles pointed (and written) by Eric Lippert, I improved the answer:


  • 更新阵列协方差的破岬

  • 增加了纯粹的代表方差

  • 增加了更多的例子从BCL

  • 添加链接到解释文章深入的概念。

  • 新增高阶函数参数协方差一个全新的部分。

  • Updated the broken-ness of array covariance
  • Added "pure" delegate variance
  • Added more examples from the BCL
  • Added links to articles that explain the concepts in-depth.
  • Added a whole new section on higher-order function parameter covariance.

返回类型的协方差

有Java(> = 5) [1] 和C ++ [2] ,在C#不支持(埃里克利珀解释的why不你可以做什么 ):

Available in Java (>= 5)[1] and C++[2], not supported in C# (Eric Lippert explains why not and what you can do about it):

class B {
    B Clone();
}

class D: B {
    D Clone();
}



接口协 [3] - 支持C#

Interface covariance[3] - supported in C#

BCL定义了通用的IEnumerable 接口是协变:

The BCL defines the generic IEnumerable interface to be covariant:

IEnumerable<out T> {...}



因此​​,下面的例子是有效的:

Thus the following example is valid:

class Animal {}
class Cat : Animal {}

IEnumerable<Cat> cats = ...
IEnumerable<Animal> animals = cats;

请注意,一个的IEnumerable 是定义只读 - 你不能添加元素,其结果
对比度,为的IList<的定义; T> 可修改如使用。新增()

Note that an IEnumerable is by definition "read-only" - you can't add elements to it.
Contrast that to the definition of IList<T> which can be modified e.g. using .Add():

public interface IEnumerable<out T> : ...  //covariant - notice the 'out' keyword
public interface IList<T> : ...            //invariant



委托协方差按法团的方式 [4] - 支持C#

Delegate covariance by means of method groups [4] - supported in C#

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 - 预方差发布文章] - 支持C#

"Pure" delegate covariance[5 - pre-variance-release article] - supported in C#

一个委托,它不带任何参数和返回的东西的BCL定义是协变的:

The BCL definition of a delegate that takes no parameters and returns something is covariant:

public delegate TResult Func<out TResult>()

这允许以下内容:

Func<Cat> getCat = () => new Cat();
Func<Animal> getAnimal = getCat; 



阵列协方差 - 在一个破碎的方式支持C#的 [6] [7]

Array covariance - supported in C#, in a broken way[6] [7]

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

和最后 - 令人惊讶的,有点令人费解的结果
委托参数协方差(是的,这是 CO 的-variance) - 高阶函数 [8]

And finally - the surprising and somewhat mind-bending
Delegate parameter covariance (yes, that's co-variance) - for higher-order functions.[8]

这需要一个参数,并且没有返回委托的BCL定义的逆变的:

The BCL definition of the delegate that takes one parameter and returns nothing is contravariant:

public delegate void Action<in T>(T obj)

包涵。让我们定义一个马戏团驯兽师 - 他可以告诉的如何的训练动物(给他一个动作与该动物的作品)

Bear with me. Let's define a circus animal trainer - he can be told how to train an animal (by giving him an Action that works with that animal).

delegate void Trainer<out T>(Action<T> trainingAction);

我们有教练的定义,让我们得到一个教练,把他的工作。

We have the trainer definition, let's get a trainer and put him to work.

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!

为了了解这一点,一个笑话为了。

In order to understand this, a joke is in order.

一个语言学教授讲课他的课一天。结果
在英语中,他说双重否定形式积极的。结果
然而,他指出,没有语言,其中双阳性可形成不利。

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."

什么的得到了与协方差办?!

What's that got to do with covariance?!

让我尝试背的最餐巾纸演示。

Let me attempt a back-of-the-napkin demonstration.

这是动作< T> 是逆变的,也就是说,它翻转类型的关系:

An Action<T> is contravariant, i.e. it "flips" the types' relationship:

A < B => Action<A> > Action<B> (1)



修改 A B 动作< A> 动作< 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)一起,我们有:

Put (1) and (2) together and we have:

,-------------(1)--------------.
 A < B => Action<A> > Action<B> => Action<Action<A>> < Action<Action<B>> (4)
         `-------------------------------(2)----------------------------'

但是,我们的教练< T> 委托实际上是一个动作<作用< T>>

But our Trainer<T> delegate is effectively an Action<Action<T>>:

Trainer<T> == Action<Action<T>> (3)



因此​​,我们可以重写(4):

So we can rewrite (4) as:

A < B => ... => Trainer<A> < Trainer<B> 



- 这,顾名思义,是指培训师是协变

- which, by definition, means Trainer is covariant.

在短,应用动作两次的我们得到禁忌禁忌变异,即类型之间的关系被翻转的两次(见(4)),所以我们又回到了协方差。

In short, applying Action twice we get contra-contra-variance, i.e. the relationship between types is flipped twice (see (4) ), so we're back to covariance.

这篇关于什么是C#中的各种协方差的? (或者,协方差:通过实施例)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆