如何在C#泛型中将子类转换为父类

3

我有一个类:

class AClass : CClass
class BClass : CClass

我需要在下面的代码中使用它们:

POJO类:

class SomeObject<T> where T : CClass
    T clazz;
    setClass(T clazz){
        this.clazz = clazz;
    .....

控制器类:

class ObjectExecutor{   

    private SomeObject<CClass> someObject;

    public ObjectExecutor execute(SomeObject<CClass> so){
        someObject = so; //exception when so - is the instance of AClass 
        return this;
    }

    public void build(){
        ....
    }
}

所以当我尝试实现它时:

SomeObject<AClass> soA = new....
soA.execute(soA) //exception point
    .build()

我得到了异常: 无法将AClass转换为CClass

因此我需要使用类似以下的东西:

class ObjectExecutor{   

    private SomeObject someObject; //without <> signature
         ..... ....

但是'C#'编译器不允许这种方式。

如何在'C#'中使用通用扩展?

截图:

我有两个类,它们之间进行了扩展:

Button extend of Adapter

接口:

Interface

实现:

enter image description here

问题发生的地方: 1.

class

2.

enter image description here

错误:

Arg "1": the type conversion from "MyTest.Terkin.ActionElement<Ranorex.Button>" in "MyTest.Terkin.ActionElement<Ranorex.Adapter>" is impossible (CS1503) - C:\Users\Valeryi\Documents\Ranorex\RanorexStudio\MyTest\Recording1.UserCode.cs:60,23

这并不是重点,但我希望你最终拼对了“Pineapple”。 - dgo
1个回答

7

这涉及到方差和协方差的问题。

协方差

在评论区讨论了您的问题后,我意识到您需要协方差,因为您正在分配比最初指定的更多的派生类。 Pineapple.Given 等待 ActionElement<Adapter>,但您正在尝试传递 ActionElement<Button>,而 Button 是从 Adapter 派生的。这意味着我们需要协变性。就像我在评论区告诉您的那样,您可以使用 out 关键字通过泛型实现协变性。请注意,只有接口和委托类型参数可以指定为变体,因此为了实现这一点,我们必须创建一个接口并在想要应用协变性时使用它。以下是代码(来自您截图的代码,我稍微修改了一下):

    class Actions { }

    class Adapter { }
    class Button : Adapter { }

    // covariant generic interface
    interface IActionElement<out T> where T : Adapter
    {
        T GetAdapter();
    }
    // covariant generic interface implementation
    class ActionElement<T> : IActionElement<T> where T : Adapter
    {
        T adapter;
        public T GetAdapter()
        {
            return adapter;
        }

        public ActionElement(T adapter)
        {
            this.adapter = adapter;
        }
    }

    class Pineapple
    {
        IActionElement<Adapter> actionElement;
        Queue<Actions> queue;

        // note that I'm using the IActionElement interface here. Not the class that implements the interface like you do
        public Pineapple Given(IActionElement<Adapter> adapter)
        {
            actionElement = adapter;
            queue = new Queue<Actions>();
            return this;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            // now it works
            Pineapple pineapple = new Pineapple();
            var pineappleClone = pineapple.Given(new ActionElement<Button>(new Button()));
        }
    }

逆变性

这是一种相反的情况,当您需要分配比原来指定的派生类更少的类时。

    class BaseClass { }
    class DerivedClassA : BaseClass { }
    class DerivedClassB : BaseClass { }

    interface ISomeObject<in T> where T : BaseClass
    {
        void SetClass(T clazz);
    }

    class SomeRealObject<T> : ISomeObject<T> where T : BaseClass
    {
        T obj;

        public void SetClass(T obj)
        {
            this.obj = obj;
        }
    }

然后你可以像这样使用它:

ISomeObject<DerivedClassA> soA = new SomeRealObject<BaseClass>();

为什么我不能使用 in T - Valentyn Hruzytskyi
那么,如果我不能在我的情况下使用 in T,你为什么建议我使用它呢? - Valentyn Hruzytskyi
@ValentynHruzytskyi 你说你想实现这个:SomeObject<AClass> soA = 什么?如果你想分配 SomeObject<BClass> - 没有办法。如果你想分配 SomeObject<CClass> 那么它是逆变的。使用 in 语句来实现。如果你想反过来做 - SomeObject<CClass> a = new SomeObject<AClass>(),我建议使用泛型的 out 参数。 - Gleb
为什么我可以使用new SomeObject<AClass>,但是“SomeObject<BClass> - 没有办法”,如果AClassBClass都扩展了相同的CClass - Valentyn Hruzytskyi
你需要明白以下内容 - a) 通常我无法使用“<in T>”这个签名,因为会出现编译器错误“意外的标记”。b) 我尝试使用“ISomeObject<CClass> a = new SomeObject<AClass>()”,但是得到了编译器错误“无法将AClass强制转换为CClass”。 - Valentyn Hruzytskyi
显示剩余10条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接