C# 中的协方差类型有哪些?(或者,协方差:举例) [英] What are the kinds of covariance in C#? (Or, covariance: by example)

查看:48
本文介绍了C# 中的协方差类型有哪些?(或者,协方差:举例)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

协方差(大致)是在使用简单"类型的复杂类型中镜像继承的能力.
例如.我们总是可以将 Cat 的实例视为 Animal 的实例.如果 ComplexType 是协变的,则 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 Lippert 指出(和撰写)的建设性评论和大量文章后,我改进了答案:

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

  • 更新了数组协方差的破坏性
  • 添加了纯"委托差异
  • 从 BCL 添加了更多示例
  • 添加了深入解释概念的文章链接.
  • 添加了一个关于高阶函数参数协方差的全新部分.

返回类型协方差:

可用于 Java (>= 5)[1] 和 C++[2],在 C# 中不受支持(Eric Lippert 解释了为什么不你能做些什么):

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# 支持

BCL 将通用的 IEnumerable 接口定义为协变:

The BCL defines the generic IEnumerable interface to be covariant:

IEnumerable<out T> {...}

因此以下示例有效:

class Animal {}
class Cat : Animal {}

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

请注意,IEnumerable 根据定义是只读"的 - 您不能向其中添加元素.
将其与可以修改的 IList 的定义形成对比,例如使用 .Add():

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# 支持

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 定义是协变的:

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)

忍耐一下.让我们定义一个马戏团的动物训练师 - 他可以被告知如何训练动物(通过给他一个与该动物一起工作的Action).

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.

Action 是逆变的,即它翻转"了类型的关系:

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

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

ActionAction改变上面的AB,得到:

Change A and B above with Action<A> and Action<B> and get:

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)----------------------------'

但是我们的Trainer委托实际上是一个Action>:

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> 

- 根据定义,这意味着 Trainer 是协变的.

- which, by definition, means Trainer is covariant.

简而言之,应用Actiontwice我们得到了contra-contra-variance,即类型之间的关系被翻转两次(见(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天全站免登陆