首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >协方差和逆变的简单示例

协方差和逆变的简单示例
EN

Stack Overflow用户
提问于 2011-01-12 22:25:45
回答 3查看 11.7K关注 0票数 42

谁能给我提供协方差、逆方差、不变性和逆不变性(如果存在的话)的简单C#示例。

到目前为止,我看到的所有示例都只是将一些对象转换到System.Object中。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2011-01-13 01:37:33

有人能给我提供协方差、逆方差、不变性和逆不变性(如果存在的话)的简单C#示例。

我不知道“逆不变性”是什么意思。剩下的都很简单。

下面是一个协方差的例子:

代码语言:javascript
复制
void FeedTheAnimals(IEnumerable<Animal> animals) 
{ 
    foreach(Animal animal in animals)
        animal.Feed();
}
...
List<Giraffe> giraffes = ...;
FeedTheAnimals(giraffes);

IEnumerable<T>接口是协变的。长颈鹿可以转换为动物的事实意味着IEnumerable<Giraffe>可以转换为IEnumerable<Animal>。由于List<Giraffe>实现了IEnumerable<Giraffe>,因此此代码在C# 4中成功;在C# 3中会失败,因为IEnumerable<T>上的协方差在C# 3中不起作用。

这应该是有意义的。长颈鹿的序列可以看作是动物的序列。

下面是一个反差的例子:

代码语言:javascript
复制
void DoSomethingToAFrog(Action<Frog> action, Frog frog)
{
    action(frog);
}
...
Action<Animal> feed = animal=>{animal.Feed();}
DoSomethingToAFrog(feed, new Frog());

Action<T>委托是逆变量。事实上,青蛙可以转换为动物,这意味着Action<Animal>可以转换为Action<Frog>。注意这个关系是如何与协变关系相反的;这就是为什么它是“反向”变量。由于可转换性,这段代码成功了;在C# 3中则会失败。

这应该是有意义的。这个动作可以接受任何动物;我们需要一个可以接受任何青蛙的动作,一个可以接受任何动物的动作肯定也可以接受任何青蛙。

不变性的一个例子:

代码语言:javascript
复制
void ReadAndWrite(IList<Mammal> mammals)
{
    Mammal mammal = mammals[0];
    mammals[0] = new Tiger();
}

我们能给这个东西传递一个IList<Giraffe>吗?不,因为有人要把一只老虎写进去,而一只老虎不能出现在长颈鹿的列表中。我们能把一个IList<Animal>传入这个东西吗?不,因为我们将从中读出一只哺乳动物,而动物列表可能包含一只青蛙。IList<T>是不变的。它只能按实际情况使用。

有关此功能设计的一些其他想法,请参阅我的系列文章,了解我们是如何设计和构建它的。

http://blogs.msdn.com/b/ericlippert/archive/tags/covariance+and+contravariance/

票数 89
EN

Stack Overflow用户

发布于 2011-01-12 22:47:17

不变性(在本文中)是指没有协方差和逆方差。所以“逆不变性”这个词没有任何意义。任何没有标记为inout的类型参数都是不变的。这意味着此类型参数既可以使用,也可以返回。

协方差的一个很好的例子是IEnumerable<out T>,因为IEnumerable<Derived>可以代替IEnumerable<Base>。或者返回T类型的值的Func<out T>

例如,可以将IEnumerable<Dog>转换为IEnumerable<Animal>,因为任何狗都是动物。

对于逆方差,您可以使用任何使用接口或委托。我想到了IComparer<in T>Action<in T>。它们从不返回T类型的变量,只接收它。在您希望接收Base的任何地方,都可以传入一个Derived

将它们视为仅限输入或仅限输出的类型参数会更容易理解IMO。

不变量一词通常不与类型方差一起使用,而是在类或方法不变量的上下文中使用,并表示一种守恒属性。参见this stackoverflow thread,其中讨论了不变量和不变性之间的区别。

票数 4
EN

Stack Overflow用户

发布于 2011-01-12 22:51:03

如果您考虑泛型的常规使用,您通常会使用接口来处理对象,但对象是类的实例-您不能实例化接口。以一个简单的字符串列表为例。

代码语言:javascript
复制
IList<string> strlist = new List<string>();

我相信您已经意识到使用IList<>而不是直接使用List<>的优点。它允许反转控制,您可能决定不再使用List<>,但希望使用LinkedList<>。上面的代码运行良好,因为接口和类的泛型类型是相同的:string

但是,如果您想要创建一个字符串列表,那么它可能会变得有点复杂。考虑这个例子:

代码语言:javascript
复制
IList<IList<string>> strlists = new List<List<string>>();

这显然无法编译,因为泛型类型参数IList<string>List<string>是不同的。即使您将外部列表声明为常规类,如List<IList<string>>,它也无法编译-类型参数不匹配。

所以这就是协方差可以提供帮助的地方。协方差允许您使用更派生的类型作为此表达式中的类型参数。如果IList<>是协变的,它将会简单地编译和修复这个问题。不幸的是,IList<>不是协变的,但它扩展的接口之一是:

代码语言:javascript
复制
IEnumerable<IList<string>> strlists = new List<List<string>>();

这段代码现在可以编译了,类型参数和上面的一样。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/4669858

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档