谁能给我提供协方差、逆方差、不变性和逆不变性(如果存在的话)的简单C#示例。
到目前为止,我看到的所有示例都只是将一些对象转换到System.Object中。
发布于 2011-01-13 01:37:33
有人能给我提供协方差、逆方差、不变性和逆不变性(如果存在的话)的简单C#示例。
我不知道“逆不变性”是什么意思。剩下的都很简单。
下面是一个协方差的例子:
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中不起作用。
这应该是有意义的。长颈鹿的序列可以看作是动物的序列。
下面是一个反差的例子:
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中则会失败。
这应该是有意义的。这个动作可以接受任何动物;我们需要一个可以接受任何青蛙的动作,一个可以接受任何动物的动作肯定也可以接受任何青蛙。
不变性的一个例子:
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/
发布于 2011-01-12 22:47:17
不变性(在本文中)是指没有协方差和逆方差。所以“逆不变性”这个词没有任何意义。任何没有标记为in或out的类型参数都是不变的。这意味着此类型参数既可以使用,也可以返回。
协方差的一个很好的例子是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,其中讨论了不变量和不变性之间的区别。
发布于 2011-01-12 22:51:03
如果您考虑泛型的常规使用,您通常会使用接口来处理对象,但对象是类的实例-您不能实例化接口。以一个简单的字符串列表为例。
IList<string> strlist = new List<string>();我相信您已经意识到使用IList<>而不是直接使用List<>的优点。它允许反转控制,您可能决定不再使用List<>,但希望使用LinkedList<>。上面的代码运行良好,因为接口和类的泛型类型是相同的:string。
但是,如果您想要创建一个字符串列表,那么它可能会变得有点复杂。考虑这个例子:
IList<IList<string>> strlists = new List<List<string>>();这显然无法编译,因为泛型类型参数IList<string>和List<string>是不同的。即使您将外部列表声明为常规类,如List<IList<string>>,它也无法编译-类型参数不匹配。
所以这就是协方差可以提供帮助的地方。协方差允许您使用更派生的类型作为此表达式中的类型参数。如果IList<>是协变的,它将会简单地编译和修复这个问题。不幸的是,IList<>不是协变的,但它扩展的接口之一是:
IEnumerable<IList<string>> strlists = new List<List<string>>();这段代码现在可以编译了,类型参数和上面的一样。
https://stackoverflow.com/questions/4669858
复制相似问题