简体   繁体   English

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

[英]How do i convert a class (which is derived from a generic "base" class) to that generic "base" class

I created a base class ("Element") and a base list class ("Elements") as generic class.我创建了一个基类(“Element”)和一个基列表类(“Elements”)作为通用类。 The generic list class should only be able to contain classes, which are of Type "Element" of derived from "Element".泛型列表类应该只能包含从“元素”派生的“元素”类型的类。 The "Element" class should own a "ParentRoot" property, which should contain the base list class ("Elements")! “Element”类应该拥有一个“ParentRoot”属性,它应该包含基本列表类(“Elements”)!

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

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

Now i create two classes and two list classes which are derived form the classes above.现在我创建了两个类和两个列表类,它们是从上面的类派生的。 But i'm failing on setting the "ParentRoot" property:但是我没有设置“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>
{
}

I get two errors at:我在以下位置收到两个错误:

ParentRoot = parent;

Cannot implicitly convert type "Ceilings" to "Elements" Cannot implicitly convert type "Walls" to "Elements"无法将类型“Ceilings”隐式转换为“Elements” 无法将类型“Walls”隐式转换为“Elements”

Is there a solution for this problem?这个问题有解决方案吗?

Thanks for any help!谢谢你的帮助!

EDIT:编辑:

OK, i have to be a bit more specific.好吧,我必须更具体一点。 I expanded the code a bit:我稍微扩展了代码:

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

I hope this makes it more precise.我希望这使它更精确。

You aren't allowed to do this because if you could, you could put the wrong kind of elements into a List .您不能这样做,因为如果可以,您可能会将错误类型的元素放入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!

If you can just treat the walls and/or ceilings as a sequence, you can do use IEnumerable<Element> instead (which works because IEnumerable<T> is "covariant"):如果您可以将墙壁和/或天花板视为一个序列,则可以使用IEnumerable<Element>代替(这是因为IEnumerable<T>是“协变的”):

IEnumerable<Element> parentRoot = ceilings; // OK

This is OK because IEnumerable<Element> has no way to modify the original collection.这是可以的,因为IEnumerable<Element>无法修改原始集合。

The problem is that, given a Generic<T> and a Child : Base , the type Generic<Base> is not a base of Generic<Child> .问题是,给定一个Generic<T>和一个Child : Base ,类型Generic<Base>不是Generic<Child> Generics are not base classes for their concrete implementations - they are templates out of which concrete implementations can be created and, in turn, the concrete implementations don't have a hierarchical relationship with each other .泛型不是它们具体实现的基类——它们是可以从中创建具体实现的模板,反过来,具体实现彼此之间没有层次关系 Consider the following snippet to understand why this is so:考虑以下片段以了解为什么会这样:

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>

Therefore, your Elements<Element> , which is a case of the Generic<Base> I described above, cannot work as a base for the others.因此,您的Elements<Element> ,这是我上面描述的Generic<Base>一种情况,不能作为其他人的基础。 Your Ceilings and Walls are neither implicitly nor explicitly convertible to Elements<Element> .您的CeilingsWalls既不能隐式也不能显式转换为Elements<Element>

A possible workaround would be to make ParentRoot a virtual or better yet an abstract property (if Element can be abstract) and override it in every subclass of Element to manually convert the Parent property to the Elements<Element> type.一种可能的解决方法是使ParentRoot成为虚拟或更好的抽象属性(如果Element可以是抽象的)Element每个子类中覆盖它以手动将Parent属性转换为Elements<Element>类型。

For example, you could change your base and your generic like this:例如,您可以像这样更改您的基础和泛型:

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

Then, for every subclass, do the following:然后,对于每个子类,执行以下操作:

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

Of course, modifications to the object returned by ParentRoot will not affect Parent .当然,对ParentRoot返回的对象的修改不会影响Parent But this is okay semantically, because (as I described with the bananas and the oranges), you wouldn't want to accidentally add a Ceiling to a Walls just because it looks like an Elements<Element> at some point in the code.但这在语义上是可以的,因为(正如我用香蕉和橙子描述的那样),您不希望仅仅因为它在代码中的某个点看起来Elements<Element>就意外地将Ceiling添加到Walls

Instead of this:取而代之的是:

Parent = parent;
ParentRoot = parent;

try this尝试这个

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

My answer is based on seeing your code and think that you are trying to build a room with n elements.Using composition "has-a" or "is-part-of" and a freely take on factory pattern i think you can achieve this.In my code i based in a "room" "has" elements,if you think "elements" "is-a" "room"?...so elements is part of the room,and those elements in your case are ceiling and wall,now wall "is-a" element and ceiling "is-a" element of room,then naturaly i derived those from element but keeping a "reference" in room for element.About having a list of rooms i nested a private class,since there is no need(in my opinion) for wall or ceiling to have access to available rooms so in room class you do all the work.In room class i derived interface IRoomBuilder with proper methods,the ones uncommented are the ones you should use for creating for example a wall with some specifications and add to room,the commented ones are just for example purposes.I placed some user-side code for you to test.我的回答是基于查看您的代码并认为您正在尝试用 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!";
        }
    }

Client-side code example:客户端代码示例:

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

Hope this helps.希望这可以帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM