协方差如何比多态更冷......而不是多余的?

时间:2011-01-04 19:36:49

标签: c# .net polymorphism covariance

.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();
        }
    }
}

3 个答案:

答案 0 :(得分:10)

考虑一个要求IContainer<Shape>

的API
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>实现了IEnumerableIEnumerable<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

但有时候它可以非常方便,因为它可以让你更好地重复使用代码。


编辑:

我忘记了 - 如果您正确使用outin关键字,可以使用{{1}}和{{1}}关键字阻止这些错误。所以没有太大的劣势。