4

基本クラス ("Element") と基本リスト クラス ("Elements") をジェネリック クラスとして作成しました。ジェネリック リスト クラスには、"Element" から派生した "Element" 型のクラスのみを含めることができます。「Element」クラスは「ParentRoot」プロパティを所有する必要があり、これには基本リスト クラス (「Elements」) が含まれている必要があります。

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

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

ここで、上記のクラスから派生した 2 つのクラスと 2 つのリスト クラスを作成します。しかし、「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>
{
}

次の場所で 2 つのエラーが発生します。

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

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>と aChild : 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>の I のケースであるあなたGeneric<Base>の は、他のベースとして機能することはできません。あなたのCeilingsandWallsは、暗黙的または明示的に に変換できませんElements<Element>

考えられる回避策は、仮想またはより良い抽象プロパティ(抽象にできる場合)を作成ParentRootし、のすべてのサブクラスでそれをオーバーライドして、プロパティを手動で型に変換することです。ElementElementParentElements<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 の ように見えるという理由だけで、誤って aCeilingに a を追加したくないからです。WallsElements<Element>

于 2013-06-14T13:04:14.357 に答える
0

私の答えは、あなたのコードを見て、あなたがn個の要素で部屋を構築しようとしていると思うことに基づいています.コンポジション「has-a」または「is-part-of」を使用し、ファクトリパターンを自由に使用すると、これを達成できると思います.私のコードでは、「要素」が「要素を持っている」に基づいています。と壁、今は壁が "is-a" 要素で、天井が "is-a" 要素の部屋です。自然にそれらを要素から派生させましたが、要素の部屋に「参照」を保持しています。壁や天井が利用可能な部屋にアクセスする必要がないため(私の意見では)、部屋のクラスではすべての作業を行います。ルーム クラス I の派生インターフェイス 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 に答える
0

これの代わりに:

Parent = parent;
ParentRoot = parent;

これを試して

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