如何将类(从通用“基础”类派生)转换为该通用“基础”类
我创建了一个基类(“Element”)和一个基类列表(“Elements”)作为generics类。 通用列表类应该只能包含类,这些类是从“Element”派生的Type“Element”。 “Element”类应该拥有一个“ParentRoot”属性,该属性应该包含基类列表类(“Elements”)!
public class Element { public Elements ParentRoot { get; set; } } public class Elements : List 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 { } public class Wall : Element { public Wall(Walls parent) { Parent = parent; ParentRoot = parent; } public Walls Parent { get; set; } } public class Walls : Elements { }
我得到两个错误:
ParentRoot = parent;
无法隐式将“Ceilings”类型转换为“Elements”无法将类型“Walls”隐式转换为“Elements”
这个问题有解决方案吗?
谢谢你的帮助!
编辑:
好的,我必须更具体一点。 我稍微扩展了代码:
public class Room { public Room(Rooms parent) { Parent = parent; } public Rooms Parent { get; set; } } public class Rooms : List { } public class Element { public Elements 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 : List 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 { 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 { public Walls(Room parent) : base(parent) { } }
我希望这会使它更精确。
您不能这样做,因为如果可以的话,您可以将错误的元素放入List
。
Elements ceilings = someCeilings; Elements parentRoot = ceilings; // Not allowed; imagine it is though. Wall wall = someWall; parentRoot.Add(wall); // Oops - we just added a wall to someCeilings!
如果您可以将墙和/或天花板视为一个序列,您可以使用IEnumerable
代替(这是因为IEnumerable
是“covariant”):
IEnumerable parentRoot = ceilings; // OK
这没关系,因为IEnumerable
无法修改原始集合。
问题是,给定Generic
和Child : Base
,类型Generic
不是Generic
。 generics不是它们具体实现的基类 – 它们是可以创建具体实现的模板 ,反过来, 具体实现彼此之间没有层次关系 。 请考虑以下代码段以了解其原因:
var bananas = List(); var fruits = (List)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
因此,您的Elements
(上面描述的Generic
的情况)不能作为其他的基础。 您的Ceilings
和Walls
既不是隐式也不是显式可转换为Elements
。
一种可能的解决方法是使ParentRoot
成为虚拟或更好的抽象属性 (如果Element
可以是抽象的) 并在Element
每个子类中覆盖它以手动将Parent
属性转换为Elements
类型。
例如,您可以像这样更改基础和通用:
public abstract class Element { public abstract Elements ParentRoot { get; } } public class Elements : List where T : Element { public Elements () : base() { } public Elements (ICollection collection) : base(collection) { } }
然后,对于每个子类,执行以下操作:
public class Wall : Element { public Wall(Walls parent) { Parent = parent; } public Walls Parent { get; set; } public override Elements ParentRoot { get { return new Elements (Parent); } } }
当然,对ParentRoot
返回的对象的修改不会影响Parent
。 但这在语义上是可以的,因为(正如我用香蕉和橙子所描述的那样),你不会因为它在代码中的某个点看起来像一个Elements
而不小心将一个Ceiling
添加到Walls
。
而不是这个:
Parent = parent; ParentRoot = parent;
试试这个
Parent = parent; ParentRoot = new Elements(); ParentRoot.AddRange(parent);
我的答案是基于看到你的代码,并认为你正在尝试建立一个有n个元素的房间。使用组合“has-a”或“is-part-of”和自由采取工厂模式我认为你可以实现这一点。在我的代码中我基于“房间”“有”元素,如果你认为“元素”“是 – ”“房间”?…所以元素是房间的一部分,你的案例中的那些元素是天花板和墙,现在墙“is-a”元素和天花板“is-a”元素的房间,然后自然我从元素派生但在元素的空间保持“参考”。关于有一个房间列表我嵌套私人因为没有必要(在我看来)墙壁或天花板可以进入可用的房间所以在房间类中你做所有的工作。在房间类我派生的界面IRoomBuilder与适当的方法,那些未注释的是你的应该用于创建例如具有一些规格的墙并添加到房间,评论的仅用于示例目的。我放置了一些用户端代码供您测试。
public interface IRooms { List AvailableRooms(); } public interface IRoomBuilder { //void MakeWall(); //void MakeWalls(int NumWalls); //void MakeCeiling(); //void MakeCeilings(int NumCeilings); void MakeElement(Element el); void MakeElements(List elmts); } public class Room:IRoomBuilder { private List roomelements; private readonly Rooms ShowRooms; public List RoomElements { get { return roomelements; } set { RoomElements.AddRange(value); } } public Room() { roomelements = new List (); ShowRooms = new Rooms(); } public void MakeElement(Element el) { RoomElements.Add(el); } public void MakeElements(List 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 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 ,IRooms { List 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 NewElements = new List { 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()); } }
希望这可以帮助。