How is covariance cooler than polymorphism…and not redundant?

后端 未结 3 968
梦谈多话
梦谈多话 2020-12-31 18:21

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

相关标签:
3条回答
  • 2020-12-31 18:29

    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.

    0 讨论(0)
  • 2020-12-31 18:33

    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.

    0 讨论(0)
  • 2020-12-31 18:46

    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.

    0 讨论(0)
提交回复
热议问题