协方差如何比多态性更酷......并且不冗余?

发布于 2024-10-10 11:44:17 字数 1609 浏览 4 评论 0原文

.NET 4 引入了协方差。我想它是有用的。毕竟,MS 历尽千辛万苦才把它添加到 C# 语言中。但是,为什么协方差比传统的多态性更有用呢?

我写这个例子是为了理解为什么我应该实现协方差,但我仍然不明白。请赐教。

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

独享拥抱 2024-10-17 11:44:17

考虑一个需要 IContainer 的 API:

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

您有一个 Container。如何将容器传递给 DrawShape API?如果没有协变,类型 Container 无法转换为 IContainer,需要您重新包装该类型或提出其他解决方法。

在使用大量通用参数的 API 中,这并不是一个罕见的问题。

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.

影子的影子 2024-10-17 11:44:17

协方差比多态性更酷,就像长耳大野兔比溜冰鞋更酷一样:它们不是同一件事。

协变和逆变(以及不变性和...全变性...任何人?)处理泛型在继承方面可以走的“方向”。在您的示例中,您正在做同样的事情,但这不是一个有意义的示例。

例如,考虑 IEnumerableout 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 实现 IEnumerableIEnumerable< /code>,不是 IEnumerable。但是,由于 IEnumerableout T,我们知道它现在兼容任何 IEnumerable 的赋值或参数传递,其中T 是 YT:Y

在某些情况下,可以在以前的版本中解决这种问题,方法是使函数本身通用并使用通用类型推断,在许多情况下产生相同的语法。然而,这并没有解决更大的问题,也绝不是 100% 的解决方法。

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.

等待圉鍢 2024-10-17 11:44:17

这是泛型版本:

object[] arr = new string[5];

我想说是否真的需要它是一个意见问题,因为它可能会引入像这样的错误时发生的错误:

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

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


编辑:

我忘了 - 如果正确使用的话,您可以通过使用 outin 关键字来防止这些错误。所以没有太大的缺点。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文