如何将类(从通用"基础"类派生)转换为该通用"基础"类

Joc*_*enk 4 .net c# generics

我创建了一个基类("元素")和一个基类列表("元素")作为泛型类.通用列表类应该只能包含类,这些类是从"Element"派生的Type"Element"."Element"类应该拥有一个"ParentRoot"属性,该属性应该包含基类列表类("Elements")!

public class Element
{
    public Elements<Element> ParentRoot { get; set; }
}

public class Elements<T> : List<T> where T : Element
{
}
Run Code Online (Sandbox Code Playgroud)

现在我创建两个类和两个列表类,它们是从上面的类派生的.但我没有设置"ParentRoot"属性:

public class Ceiling : Element
{
    public Ceiling(Ceilings parent)
    {
        Parent = parent;
        ParentRoot = parent;
    }

    public Ceilings Parent { get; set; }
}

public class Ceilings : Elements<Ceiling>
{
}

public class Wall : Element
{
    public Wall(Walls parent)
    {
        Parent = parent;
        ParentRoot = parent;
    }

    public Walls Parent { get; set; }
}

public class Walls : Elements<Wall>
{
}
Run Code Online (Sandbox Code Playgroud)

我得到两个错误:

ParentRoot = parent;
Run Code Online (Sandbox Code Playgroud)

无法将"Ceilings"类型隐式转换为"Elements",无法将类型"Walls"隐式转换为"Elements"

有这个问题的解决方案吗?

谢谢你的帮助!

编辑:

好的,我必须更具体一点.我稍微扩展了代码:

public class Room
{
    public Room(Rooms parent)
    {
        Parent = parent;
    }

    public Rooms Parent { get; set; }
}

public class Rooms : List<Room>
{

}

public class Element
{
    public Elements<Element> ParentRoot { get; set; }

    public Rooms FindRoomsToElement()
    {
        Rooms rooms = new Rooms();

        foreach (Room room in ParentRoot.Parent.Parent)
        {
            // Do stuff here

            // if i rename the "ParentRoot" property to "Parent" and make it "virtual",
            // and the other properties overwrite it with the "new" key, then this will
            // get a null exception!
            // i haven't testet it, but i think abstrakt will bring the same/similar result

            // if i make the "ParentRoot" property IEnumerable, then there will no
            // ParentRoot.Parent be available
        }

        return rooms;
    }
}

public class Elements<T> : List<T> where T : Element
{
    public Elements(Room parent)
    {
        Parent = parent;
    }

    public Room Parent { get; set; }
}



public class Ceiling : Element
{
    public Ceiling(Ceilings parent)
    {
        Parent = parent;
        //ParentRoot = parent;
    }

    public Ceilings Parent { get; set; }
}

public class Ceilings : Elements<Ceiling>
{
    public Ceilings(Room parent) : base(parent)
    {
    }
}

public class Wall : Element
{
    public Wall(Walls parent)
    {
        Parent = parent;
        //ParentRoot = parent;
    }

    public Walls Parent { get; set; }
}

public class Walls : Elements<Wall>
{
    public Walls(Room parent) : base(parent)
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

我希望这会使它更精确.

Mat*_*son 8

你不能这样做,因为如果可以的话,你可以将错误的元素放入List.

 Elements<Ceilings> ceilings = someCeilings;
 Elements<Element> parentRoot = ceilings; // Not allowed; imagine it is though.
 Wall wall = someWall;
 parentRoot.Add(wall); // Oops - we just added a wall to someCeilings!
Run Code Online (Sandbox Code Playgroud)

如果您可以将墙和/或天花板视为一个序列,您可以IEnumerable<Element>改为使用(因为它IEnumerable<T>是"协变的"):

IEnumerable<Element> parentRoot = ceilings; // OK
Run Code Online (Sandbox Code Playgroud)

这没关系,因为IEnumerable<Element>无法修改原始集合.


The*_*kis 5

问题是,给定 aGeneric<T>和 a Child : Base,该类型Generic<Base>不是 的基数Generic<Child>。泛型不是其具体实现的基类 -它们是可以创建具体实现的模板,反过来,具体实现彼此之间没有层次关系。请考虑以下代码片段以了解为什么会这样:

var bananas = List<Banana>();
var fruits = (List<Fruit>)bananas; // If this was valid
fruits.Add(new Orange()); // Then this would be valid
// So we'd have an Orange to a list of runtime type List<Banana>
Run Code Online (Sandbox Code Playgroud)

因此,你的Elements<Element>,即上述 I 的情况Generic<Base>,不能作为其他人的基础。您的CeilingsWalls既不能隐式也不能显式转换为Elements<Element>.

一种可能的解决方法是创建ParentRoot一个虚拟的或更好的抽象属性(如果Element可以是抽象的) ,并在 的每个子类中重写它Element,以手动将属性转换ParentElements<Element>类型。

例如,您可以像这样更改您的基础和通用:

public abstract class Element
{
    public abstract Elements<Element> ParentRoot { get; }
}

public class Elements<T> : List<T> where T : Element
{
    public Elements<T>() : base()
    {
    }
    public Elements<T>(ICollection<T> collection) : base(collection)
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,对于每个子类,执行以下操作:

public class Wall : Element
{
    public Wall(Walls parent)
    {
        Parent = parent;
    }

    public Walls Parent { get; set; }
    public override Elements<Element> ParentRoot
    {
        get
        {
            return new Elements<Element>(Parent);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

当然,对返回的对象的修改ParentRoot不会影响Parent. 但这在语义上是可以的,因为(正如我用香蕉和橙子描述的那样),您不想仅仅因为它在代码中的某个时刻看起来 像 an就意外地将 a 添加Ceiling到 a 中。WallsElements<Element>