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

时间:2013-06-14 12:53:47

标签: c# .net generics

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

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

public class Elements<T> : List<T> where T : Element
{
}

现在我创建两个类和两个列表类,它们是从上面的类派生的。但是我没有设置“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>
{
}

我收到两个错误:

ParentRoot = parent;

无法将“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)
    {
    }
}

我希望这会让它更精确。

4 个答案:

答案 0 :(得分: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!

如果您可以将墙和/或天花板视为一个序列,则可以使用IEnumerable<Element>代替(因为IEnumerable<T>是“协变”而有效):

IEnumerable<Element> parentRoot = ceilings; // OK

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

答案 1 :(得分:4)

问题是,在Generic<T>Child : Base的情况下,Generic<Base>类型不是Generic<Child>的基础。泛型不是其具体实现的基类 - 它们是模板,其中可以创建具体实现,反过来,具体实现彼此之间没有层次关系< / strong>即可。请考虑以下代码段以了解其原因:

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>

因此,您Elements<Element>(我上面描述的Generic<Base>的情况)无法作为其他人的基础。您的CeilingsWalls既不会隐式也无法明确转换为Elements<Element>

可能的解决方法是使ParentRoot成为虚拟或更好的抽象属性(如果Element可以是抽象的)并覆盖它Element的每个子类中手动将Parent属性转换为Elements<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)
    {
    }
}

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

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

当然,ParentRoot返回的对象的修改不会影响Parent。但这在语义上是可以的,因为(正如我用香蕉和橘子所描述的那样),你不会因为看起来Ceiling添加到Walls >在代码中的某个位置像Elements<Element>一样。

答案 2 :(得分:0)

而不是:

Parent = parent;
ParentRoot = parent;

试试这个

Parent = parent;
ParentRoot = new Elements<Element>();
ParentRoot.AddRange(parent);

答案 3 :(得分:0)

我的回答是基于查看您的代码并认为您正在尝试使用n个元素构建一个房间。使用组合&#34; has-a&#34;或&#34;是&#34;的一部分&#34;并且可以自由地采用工厂模式,我认为你可以实现这一目标。在我的代码中,我基于一个&#34;房间&#34; &#34;具有&#34;元素,如果你认为&#34;元素&#34; &#34;是-A&#34; &#34;房间&#34;?...所以元素是房间的一部分,你的情况下的那些元素是天花板和墙壁,现在墙壁是&#34; is-a&#34;元素和天花板&#34; is-a&#34;房间的元素,然后naturaly我从元素派生的,但保持&#34;参考&#34;因为没有(在我看来)墙壁或天花板可以进入可用房间所以在房间类中你做了所有的工作。在房间类我使用适当的方法导出了界面IRoomBuilder,未使用的那些是您应该用于创建例如具有一些规格的墙并添加到房间的那些,评论的那些仅用于示例目的。我为您放置了一些用户端代码测试

    public interface IRooms
    {
        List<Room> AvailableRooms();
    }

    public interface IRoomBuilder
    {
        //void MakeWall();
        //void MakeWalls(int NumWalls);
        //void MakeCeiling();
        //void MakeCeilings(int NumCeilings);
        void MakeElement(Element el);
        void MakeElements(List<Element> elmts);
    }

    public class Room:IRoomBuilder
    {
        private List<Element> roomelements;
        private readonly Rooms ShowRooms;

        public List<Element> RoomElements
        {
            get { return roomelements; }
            set { RoomElements.AddRange(value); }
        }

        public Room()
        {
            roomelements = new List<Element>();
            ShowRooms = new Rooms();
        }

        public void MakeElement(Element el)
        {
            RoomElements.Add(el);
        }

        public void MakeElements(List<Element> elmts)
        {
            RoomElements.AddRange(elmts);
        }

        //public void MakeWall()
        //{

        //    RoomElements.Add(Element.MakeElement(typeof(Wall).Name));
        //}

        //public void MakeWalls(int NumWalls)
        //{
        //    for (int i = 0; i < NumWalls; i++)
        //    {
        //        RoomElements.Add(Element.MakeElement(typeof(Wall).Name));
        //    }
        //}

        //public void MakeCeiling()
        //{
        //    RoomElements.Add(Element.MakeElement(typeof(Ceiling).Name));
        //}

        //public void MakeCeilings(int NumCeilings)
        //{
        //    for (int i = 0; i < NumCeilings; i++)
        //    {
        //        RoomElements.Add(Element.MakeElement(typeof(Ceiling).Name));
        //    };
        //}

        public void AddRoom()
        {
            ShowRooms.Add(this);
        }

        public List<Room> GetAllRooms()
        {
            IRooms r = (IRooms)ShowRooms;
            return r.AvailableRooms();
        }

        public override string ToString()
        {
            return "I am a room with " + RoomElements.Count.ToString() + " Elements";
        }

        private class Rooms : List<Room>,IRooms
        {  
            List<Room> IRooms.AvailableRooms()
            {
                return this;
            }
        }
    }

    public abstract class Element
    {
        //this method is used for the commented methods
        public static Element MakeElement(string name)
        {
            if (name == typeof(Ceiling).Name)
                return new Ceiling() as Element;
            else if (name == typeof(Wall).Name)
                return new Wall() as Element;
            else
                throw new ArgumentException("Parameter not valid");
        }
    }

    public class Ceiling : Element
    {
        //your implementation.

        public override string ToString()
        {
            return "I am a ceiling";
        }
    }

    public class Wall : Element
    {
        //your implementation.

        public override string ToString()
        {
            return "I am a wall!";
        }
    }

客户端代码示例:

            Wall w = new Wall();
            Ceiling c = new Ceiling();
            Room r = new Room();
            r.MakeElement(w);
            r.MakeElement(c);
            List<Element> NewElements = new List<Element>{ new Wall(), new Ceiling() };
            r.MakeElements(NewElements);
            //r.MakeWalls(5);
            //r.MakeCeilings(6);
            r.AddRoom();
            foreach (Room room in r.GetAllRooms())
            {
                MessageBox.Show(room.ToString());
                foreach (Element el in room.RoomElements)
                {
                    MessageBox.Show(el.ToString());
                }
            }

希望这有帮助。