【问题标题】:C# covariance structure understanding?C#协方差结构理解?
【发布时间】:2012-02-10 14:41:57
【问题描述】:
  • 假设

    A 级 { }

    B 类:A { }

泛型类不支持协方差。

意思-我们不能这样做:

MyConverter<B> x1= new MyConverter<B>();
MyConverter<A> x2= x1;  

很好理解。

根据我的阅读 - 我知道协方差将可用:

“如果您使用在 Generic Class 上实现的支持 Generic Interface - 这样就可以通过这些接口访问 T 类型对象实例”。

我只有一个问题。

我见过很多“转换器”类的例子,它是Stack 的一种形式。

但从未理解“如果我只想使用 A 的引用中的 1 个 B 实例怎么办?”

所以我尝试了一些代码:

创建B对象+值--->为B使用通用转换器---> 使用协方差流来获取它的A 参考---> 现在你可以使用它了 作为 A 或作为 B。

我的问题:

这是正确的做法吗(仅对 1 个对象使用协方差)?

附言 代码工作正常,编译正常。 http://i.stack.imgur.com/PJ6QO.png

我最近一直在询问/阅读很多关于这个主题的内容 - 我深入研究事物以便尽我所能理解它们。

【问题讨论】:

    标签: c# .net-4.0 covariance


    【解决方案1】:

    您的代码可以编译并且可以工作,那么它“正确”吗?我想是的!

    但是,只有一个元素的堆栈并不是很有趣;那不是真正的堆栈。让我们考虑一下如何制作真正的协变和逆变堆栈。

    interface IPush<in T> { void Push(T item); }
    interface IPop<out T> { T Pop(); }
    class Stack<T> : IPush<T>, IPop<T>
    {
        private class Link
        {
            public T Item { get; private set; }
            public Link Next { get; private set; }
            public Link(T item, Link next) { this.Item = item; this.Next = next; }
        }
    
        private Link head;
        public Stack() { this.head = null; }
    
        public void Push(T item)
        {
            this.head = new Link(item, this.head);
        }
    
        public T Pop()
        {
            if (this.head == null) throw new InvalidOperationException();
            T value = this.head.Item;
            this.head = this.head.Next;
            return value;
        }
    }
    

    现在您可以将堆栈协变地用于弹出,逆变器用于推送:

    Stack<Mammal> mammals = new Stack<Mammal>();
    IPop<Animal> animals = mammals;
    IPush<Giraffe> giraffes = mammals;
    IPush<Tiger> tigers = mammals;
    giraffes.Push(new Giraffe());
    tigers.Push(new Tiger());
    System.Console.WriteLine(animals.Pop()); // Tiger
    System.Console.WriteLine(animals.Pop()); // Giraffe
    

    如果我只想从 A 的引用中使用 B 的一个实例怎么办?

    您的问题是“如果我想使用 Tiger 但我有一个 Animal 参考怎么办?”答案是“你不能”,因为动物可能不是老虎!如果你想测试对 Animal 的引用是否真的是老虎,那么说:

    Tiger tiger = myAnimal as Tiger;
    if (tiger != null) ...
    

    if (myAnimal is Tiger) ...
    

    如果你想把C&lt;B&gt;类转换成C&lt;A&gt;呢?

    这是不可能的。那里没有参考转换。 C# 4 中唯一的协变和逆变引用转换是在泛型接口和泛型委托上,使用引用类型作为类型参数构造。泛型类和结构不能协变或逆变使用。你能做的最好的事情就是让类实现一个变体接口

    【讨论】:

    • 嗨 :) 这正是所有示例(大多数)都是堆栈的问题。 Tahts 为什么我写了what if I want to use only 1 instance of B from a reference of A ? 你能参考一下这个情况吗?并非在所有情况下我都需要堆栈......有时我只需要一个元素的单次转换......?......(这就是为什么我称之为类Myconverter而不是MySingleStack
    • 我不认为通用转换类是处理转换的正确方法。想要将B 转换为A?使用B as A(A) B
    • @RoyiNamir:好的,让我们考虑一个桶。一桶什么只能装苹果的桶不能用作水果桶,因为您可以将橙子放入水果桶,但不能一桶苹果。一桶水果不能用作只能装苹果的桶,因为那桶水果里可能有一个橙子
    • @RoyiNamir:你的愿望很容易实现。每天 8 小时、每周 5 天、每年 48 周、5 到 10 年仔细研究一门语言及其编译器的设计和实现,您也可以对您选择的任何编程语言有相当的了解。仅此而已。
    • @stt106:类型推断将推断您打算调用Wash&lt;Bear&gt;(Stack&lt;Bear&gt;),并且Bear 匹配约束:BearAnimal 的子类型。那里没有一般的差异。您永远不会尝试将一堆熊用作一堆动物。您正在尝试参数化一个可由熊类型的任何动物类型参数化的方法。
    【解决方案2】:

    看起来您使用转换器只是为了获取指向B 类型对象的A 类型的引用。有一种更简单的方法可以做到这一点,称为强制转换:

    B b = new B();
    A a = (A)b;
    

    实际上,由于 A 是 B 的超类,所以转换是隐式的:

    B b = new B();
    A a = b;
    

    您的程序可能是:

    class Program
    {
        static void Main(string[] args)
        {
            B b = new B { b1 = 22222 };
            A a = b;
            Console.WriteLine(a.a1);
            Console.WriteLine(((B)a).b1);
        }
    }
    

    【讨论】:

    • 是的,但整个示例是为了展示我们如何通过泛型接口使用泛型类解决/克服类型安全问题...
    • 但是使用协方差并不比这更安全。协方差表示如果 B 是 A 的子类型,则 X 是 X 的子类型。要从 A 到 B,您需要垂头丧气;要从 X 到 X,您需要向下转型。
    • 好的,但是您可以使用通用列表来做到这一点!如果没有带有输入或输出的通用接口,您将无法制作list&lt;animals&gt; a =... list&lt;bears&gt;;。你必须创建一个中间类来实现这些通用接口......
    • 好的,我想我明白你现在的意思了。
    【解决方案3】:
    IPushable<B> x1 = new MyConverter<B>();
    x1.Set(b);
    // I believe this is valid.
    IPoppable<A> final = x2;
    

    您可以在 this blog 上找到一些很好的示例和说明。

    【讨论】:

    • 代码有效且已编译。请在 Console.WriteLine 之后查看结果...i.stack.imgur.com/PJ6QO.png
    • 嗯。我不太明白,但我会相应地编辑我的答案。
    • 我不太确定问题是什么,那么? “有问题的正确”并没有真正的意义。
    • @zenxer。这是 not 逆变。它的协方差。协方差是:假设 B 继承了 A,因此如果 X 允许引用 X,则类型 X 是协方差
    • 对不起,@RoyiNamir;我只是不明白你的问题。由于链接很棒,我将在此处留下我的答案(已编辑)。
    猜你喜欢
    • 1970-01-01
    • 2018-04-27
    • 2018-08-22
    • 2017-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-18
    相关资源
    最近更新 更多