C# 协方差结构理解吗?

发布于 2025-01-04 08:36:53 字数 1012 浏览 1 评论 0原文

  • 假设

    A级 { }

    B 级:A { }

泛型类不支持

协方差。意思是 - 我们不能做这样的事情:

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

很好并且可以理解。

从我的阅读中,我了解到协方差将可用:

“如果您使用在通用类上实现的支持通用接口,则可以通过这些接口访问 T 类型对象实例”。

我只有一个问题。

我见过许多以 Stack 形式呈现“转换器”类的示例。

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

所以我尝试了一些代码:

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

在此处输入图像描述

在此处输入图像描述

我的问题:

这是执行此操作的正确方法吗(仅对 1 个对象使用协方差)?

ps 代码工作正常并且编译正常。 https://i.sstatic.net/PJ6QO.png

我一直在问/读很多东西最近关于这个话题——我深入研究事物,以便尽可能地理解它们。

  • Assuming

    class A
    { }

    class B : A
    { }

covariance is not supported for generic class.

Meaning - we cant do something like this :

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

Thats fine and understood.

From my reading - i understand that Covariance will be available:

"If you use a backing Generic Interface Being implemented on a Generic Class - so that access to the T type object instance will be available through those interfaces ".

I have just one problem.

Ive seen many examples of the "converter" class as a form of Stack .

But never understood " what if I want to use only 1 instance of B from a reference of A ? "

so Ive tried some code :

Create B object + values ---> use Generic Converter for B --->
use the covariance flow to get its A reference ---> now you can use it
either as A or as B.

enter image description here

enter image description here

My question :

Is That the correct way of doing this ( for using covariance for 1 object only ) ?

p.s.
The code is working and compiled ok. https://i.sstatic.net/PJ6QO.png

Ive been asking /reading a lot about this topic lately - I dive into things in order to understand them the best I can.

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

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

发布评论

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

评论(3

隔纱相望 2025-01-11 08:36:53

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

然而,拥有一个仅包含单个元素的堆栈并不是很有趣;这并不是真正的堆栈。让我们考虑一下如何创建一个真正的协变和逆变堆栈。

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 的一个实例该怎么办?

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

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

if (myAnimal is Tiger) ...

如果您想将类 C 转换为 C 该怎么办?


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

Your code compiles and works, so is it "correct"? I guess it is!

However it is not very interesting having a stack that only contains a single element; that's not really a stack. Let's think about how you might make a truly covariant and contravariant stack.

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

And now you can use the stack covariantly for popping, and contravariantly for pushing:

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

What if I want to use only one instance of B from a reference of A?

Your question is "what if I want to use a Tiger but I have a reference an Animal?" The answer is "you can't" because the Animal might not be a Tiger! If you want to test whether the reference to Animal is really a tiger then say:

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

or

if (myAnimal is Tiger) ...

What about if you want to convert class C<B> to C<A>?

That's not possible. There is no reference conversion there. The only covariant and contravariant reference conversions in C# 4 are on generic interfaces and generic delegates that are constructed with reference types as the type arguments. Generic classes and structs may not be used covariantly or contravariantly. The best thing you can do is make the class implement a variant interface.

南渊 2025-01-11 08:36:53

看起来您使用转换器只是为了获取指向 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);
    }
}

It looks like you're using the converter simply to get a reference of type A pointing to an object of type B. There's a much easier way to do that, called casting:

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

In fact, since A is a superclass of B, the conversion is implicit:

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

Your program could be:

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);
    }
}
累赘 2025-01-11 08:36:53
IPushable<B> x1 = new MyConverter<B>();
x1.Set(b);
// I believe this is valid.
IPoppable<A> final = x2;

您可以在 此博客

IPushable<B> x1 = new MyConverter<B>();
x1.Set(b);
// I believe this is valid.
IPoppable<A> final = x2;

You can find some great examples and descriptions of it on this blog.

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