Как ковариация круче полиморфизма ... и не избыточна? - PullRequest
9 голосов
/ 04 января 2011

.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 ]

10 голосов
/ 04 января 2011

Рассмотрим API, который запрашивает IContainer<Shape>:

public void DrawShape(IContainer<Shape> container>) { /* ... */ }

У вас есть Container<Circle>.Как вы можете передать свой контейнер в DrawShape API?Без ковариации тип Container<Circle> не может быть преобразован в IContainer<Shape>, что требует от вас переупорядочения типа или поиска другого обходного пути.

Это не редкая проблема в API, которые используют много общегопараметры.

5 голосов
/ 04 января 2011

Ковариация круче, чем полиморфизм, так же, как крольчихи круче, чем катание на коньках: это не одно и то же.

Ковариация и контравариантность (а также инвариантность и ... всеядность ... кто-нибудь?) Имеют дело с "направлением", которым могут следовать дженерики в отношении наследования. В вашем примере вы делаете то же самое, но это не значимый пример.

Рассмотрим, например, тот факт, что 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% обходным путем.

0 голосов
/ 04 января 2011

Это универсальная версия:

object[] arr = new string[5];

Я бы сказал, нужно ли это на самом деле или нет, это вопрос мнения, так как он может содержать ошибки, подобные тем, которые случаются при произнесении таких вещей, как:

arr[0] = new object(); //Run-time error

Но иногда это может быть очень удобно, поскольку позволяет лучше повторно использовать код.


Edit:

Я забыл - вы можете предотвратить эти ошибки, используя ключевые слова out и in, если используете их правильно. Так что недостатка не так много.

...