4

我创建了一个基类(“Element”)和一个基列表类(“Elements”)作为泛型类。通用列表类应该只能包含从“元素”派生的“元素”类型的类。“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;

无法将“天花板”类型隐式转换为“元素” 无法将“墙”类型隐式转换为“元素”

这个问题有解决方案吗?

谢谢你的帮助!

编辑:

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

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

4 回答 4

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>无法修改原始集合。

于 2013-06-14T13:09:15.040 回答
4

问题是,给定 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>

因此,您的Elements<Element>,这是Generic<Base>我上面描述的情况,不能作为其他人的基础。您的CeilingsandWalls既不能隐式也不能显式转换为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. 但这在语义上是可以的,因为(正如我用香蕉和橙子所描述的那样),您不希望仅仅因为在代码中的某个点看起来 像a 就意外地将 a 添加Ceiling到 a中。WallsElements<Element>

于 2013-06-14T13:04:14.357 回答
0

而不是这个:

Parent = parent;
ParentRoot = parent;

尝试这个

Parent = parent;
ParentRoot = new Elements<Element>();
ParentRoot.AddRange(parent);
于 2013-06-14T13:35:05.757 回答
0

我的回答是基于查看您的代码并认为您正在尝试使用 n 个元素构建一个房间。使用组合“has-a”或“is-part-of”以及自由采用工厂模式,我认为您可以实现这一点.在我的代码中,我基于一个“房间”“有”元素,如果你认为“元素”“是一个”“房间”?...所以元素是房间的一部分,而你的案例中的那些元素是天花板和墙壁,现在墙壁“is-a”元素和天花板“is-a”房间元素,然后自然我从元素中派生出这些元素,但在房间中为元素保留“参考”。关于有一个房间列表,我嵌套了一个私人上课,因为没有必要(在我看来)墙壁或天花板可以进入可用的房间,所以在房间课上你做所有的工作。在房间类中,我使用适当的方法派生了接口 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());
                }
            }

希望这可以帮助。

于 2013-06-15T17:17:56.827 回答