How is covariance cooler than polymorphism…and not redundant?

雨燕双飞 提交于 2020-01-21 01:44:05

问题


.NET 4 introduces covariance. I guess it is useful. After all, MS went through all the trouble of adding it to the C# language. But, why is Covariance more useful than good old polymorphism?

I wrote this example to understand why I should implement Covariance, but I still don't get it. Please enlighten me.

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

回答1:


Consider an API which asks for an IContainer<Shape>:

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

You have a Container<Circle>. How can you pass your container to the DrawShape API? Without covariance, the type Container<Circle> is not convertible to IContainer<Shape>, requiring you to rewrap the type or come up with some other workaround.

This is not an uncommon problem in APIs that use a lot of generic parameters.




回答2:


Covariance is cooler than polymorphism in the same way that jackrabbits are cooler than iceskates: they're not the same thing.

Covariance and contravariance (and invariance and...omnivariance...anybody?) deal with the "direction" that generics can go with regard to inheritance. In your example, you're doing the same thing, but that's not a meaningful example.

Consider, for example, the fact that IEnumerable<T> is out T. This lets us do something like this:

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

In previous versions of C#, this would have been impossible, as List<string> implements IEnumerable and IEnumerable<string>, not IEnumerable<object>. However, since IEnumerable<T> is out T, we know that it's now compatible for assignment or parameter passing for any IEnumerable<Y> where T is Y or T:Y.

This sort of thing could be worked around in previous versions under some circumstances by making the function itself generic and using generic type inference, yielding identical syntax in many cases. This, however, did not solve the larger problem and was by no means a 100% workaround.




回答3:


It's the generics version of:

object[] arr = new string[5];

I'd say whether or not it's actually needed is a matter of opinion, since it can introduce bugs like those that happen when saying things like:

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

But sometimes it can be very handy, since it lets you re-use code better.


Edit:

I'd forgotten -- you can prevent those bugs by using the out and in keywords, if you use them correctly. So there's not much of a disadvantage.



来源:https://stackoverflow.com/questions/4597612/how-is-covariance-cooler-than-polymorphism-and-not-redundant

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!