.NET 4引入了协方差。我想这很有用。毕竟,MS经历了将其添加到C#语言的所有麻烦。但是,为什么协方差比良好的旧多态更有用呢?
我写了这个例子来理解为什么我应该实现Covariance,但我仍然没有得到它。请赐教。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Sample
{
class Demo
{
public delegate void ContraAction<in T>(T a);
public interface IContainer<out T>
{
T GetItem();
void Do(ContraAction<T> action);
}
public class Container<T> : IContainer<T>
{
private T item;
public Container(T item)
{
this.item = item;
}
public T GetItem()
{
return item;
}
public void Do(ContraAction<T> action)
{
action(item);
}
}
public class Shape
{
public void Draw()
{
Console.WriteLine("Shape Drawn");
}
}
public class Circle:Shape
{
public void DrawCircle()
{
Console.WriteLine("Circle Drawn");
}
}
public static void Main()
{
Circle circle = new Circle();
IContainer<Shape> container = new Container<Circle>(circle);
container.Do(s => s.Draw());//calls shape
//Old school polymorphism...how is this not the same thing?
Shape shape = new Circle();
shape.Draw();
}
}
}
答案 0 :(得分:10)
考虑一个要求IContainer<Shape>
:
public void DrawShape(IContainer<Shape> container>) { /* ... */ }
你有Container<Circle>
。如何将容器传递到DrawShape
API?如果没有协方差,Container<Circle>
类型不能转换为IContainer<Shape>
,要求您重新打包该类型或提出其他一些解决方法。
在使用大量通用参数的API中,这不是一个罕见的问题。
答案 1 :(得分:5)
协方差比多态性更酷,就像长耳大衣比iceskates更冷:它们不是一回事。
协方差和逆变(以及不变性和......无所不在......任何人?)处理泛型可以继承的“方向”。在你的例子中,你做的是同样的事情,但这不是一个有意义的例子。
例如,考虑IEnumerable<T>
为out T
的事实。这让我们可以这样做:
public void PrintToString(IEnumerable<object> things)
{
foreach(var obj in things)
{
Console.WriteLine(obj.ToString());
}
}
public static void Main()
{
List<string> strings = new List<string>() { "one", "two", "three" };
List<MyClass> myClasses = new List<MyClass>();
// add elements to myClasses
PrintToString(strings);
PrintToString(myClasses);
}
在以前的C#版本中,这是不可能的,因为List<string>
实现了IEnumerable
和IEnumerable<string>
,不是 IEnumerable<object>
。但是,由于IEnumerable<T>
为out T
,我们知道它现在与IEnumerable<Y>
或T is Y
的任何T:Y
的分配或参数传递兼容。
通过使函数本身具有通用性并使用泛型类型推断,在许多情况下产生相同的语法,可以在某些环境下的先前版本中解决这种问题。然而,这并没有解决更大的问题,绝不是100%的解决方法。
答案 2 :(得分:-1)
这是仿制药版本:
object[] arr = new string[5];
我会说它实际上是否需要是一个意见问题,因为它可能会引入类似以下内容时发生的错误:
arr[0] = new object(); //Run-time error
但有时候它可以非常方便,因为它可以让你更好地重复使用代码。
编辑:
我忘记了 - 如果您正确使用out
和in
关键字,可以使用{{1}}和{{1}}关键字阻止这些错误。所以没有太大的劣势。