15

さて、これが状況です。私はFlexCollection<T>クラスを持っています。その目的は、のいくつかの専門分野のリストを保持することFlexItemです。したがって、次のようになります。

public class FlexCollection<T> where T : FlexItem, new()
{
    public void Add(T item) { ... }
    ...
}

FlexItemジェネリッククラス自体ではありません。私が達成したかったFlexItemのは、オブジェクトを含むコレクションへの参照をフィールドに保持する機能です。残念ながら、C#では、テンプレートクラスの「任意の」特殊化への参照を保持することはできません(Javaの場合のように)。最初は非ジェネリックインターフェイスを使用しようとしましIFlexCollectionたが、実際には各メソッドを2回実装する必要がありました。

public class FlexCollection<T> : IFlexCollection where T : FlexItem, new()
{
    public void Add(T item) { ... } // to use in generic calls
    public void Add(object item) { ... } // to use for calls from within FlexItem
    ...
}

それから、FlexItem自体をジェネリッククラスにできることを知りました。次に、スペシャライゼーションは、このスペシャライゼーションのオブジェクトのコレクションへの参照を保持できます(これは非常に論理的です)。したがって:

public class FlexItem<T> where T : FlexItem<T>, new()
{
    public FlexCollection<T> ReferenceToParentCollection;
    ...
}

public class FlexCollection<T> where T : FlexItem<T>, new()
{
    public void Add(T item) { ... } 
    ...
}

今、私はいくつかのFlexItem<T>専門分野と対応するコレクションを宣言することができます:

public class BasicItem : FlexItem<BasicItem> { public int A; }
public class BasicCollection : FlexCollection<BasicItem> { };

これらのクラスを拡張して追加のフィールドを保持しようとすると、問題が発生します。つまり、フィールドに加えてフィールドを保持するExtendedItemクラスが必要でした:BA

public class ExtendedItem : BasicItem { public int B; }
public class ExtendedCollection : FlexCollection<ExtendedItem> { };

そして、それExtendedItemはのサブクラスでFlexItem<BasicItem>あり、ではありませんFlexItem<ExtendedItem>。したがって、上記のように宣言することは不可能ExtendedCollectionです。これにより、コンパイルエラーが発生します。

The type 'Demo.ExtendedItem' must be convertible to
'Demo.FlexItem<Demo.ExtendedItem>' in order to use it as parameter 'T'
in the generic type or method 'Demo.BasicCollection<T>'

そのようなタイプの衝突を回避する方法はありますか?

4

3 に答える 3

1

これを解決するには、次の 2 つの方法があります。

ベース クラス コレクションを継承せずに、ベース ポリモーフィック コレクションを使用できます。

class Program
{
    static void Main(string[] args)
    {
        BasicCollection extendedCollection = new BasicCollection();
        extendedCollection.Add(new ExtendedItem { A = 1, B = 2});
        extendedCollection.Add(new BasicItem { A = 3 });
        extendedCollection.Add(new ExtendedItem { A = 4, B = 5});

        foreach (BasicItem item in extendedCollection)
        {
            switch(item.GetType().Name)
            {
                case "BasicItem":
                    Console.Out.WriteLine(string.Format("Found BasicItem: A={0}", item.A));
                    break;
                case "ExtendedItem":
                    ExtendedItem extendedItem = item as ExtendedItem;
                    Console.Out.WriteLine(string.Format("Found ExtendedItem: A={0} B={1}", extendedItem.A, extendedItem.B));
                    break;
            }
        }
    }
}

public class FlexItem<T> where T : FlexItem<T>, new()
{
    public FlexCollection<BasicItem> ReferenceToParentCollection;
}

public class FlexCollection<T> where T : FlexItem<T>, new()
{
    public void Add(T item) { } 
}
public class BasicItem : FlexItem<BasicItem> { public int A; }
public class ExtendedItem : BasicItem { public int B; }
public class BasicCollection : FlexCollection<BasicItem>
{
    Collection<BasicItem> items = new Collection<BasicItem>();
    public void Add(BasicItem item)
    {
        item.ReferenceToParentCollection = this;
        items.Add(item);
    }
    public void Remove(BasicItem item)
    {
        item.ReferenceToParentCollection = null;
        items.Remove(item);
    }
    public IEnumerator GetEnumerator()
    {
        return items.GetEnumerator();
    }

}

または、子オブジェクトの型がわかっているので、コレクション クラス参照をボックス化して、必要なときにボックス化解除できますか?

class Program
{
    static void Main(string[] args)
    {
        ExtendedCollection extendedCollection = new ExtendedCollection();
        extendedCollection.Add(new ExtendedItem { A = 1, B = 2, ReferenceToParentCollection = extendedCollection });
        extendedCollection.Add(new ExtendedItem { A = 3, B = 3, ReferenceToParentCollection = extendedCollection });
        foreach (ExtendedItem item in extendedCollection)
        {
            (item.ReferenceToParentCollection as ExtendedCollection) ...
        }
    }
}


public class FlexItem<T> where T : FlexItem<T>, new()
{
    public object ReferenceToParentCollection;
}

public class FlexCollection<T> where T : FlexItem<T>, new()
{
    public void Add(T item) { } 
}

public class BasicItem : FlexItem<BasicItem> { public int A; }
public class BasicCollection : FlexCollection<BasicItem> { };
public class ExtendedItem : BasicItem { public int B; }
public class ExtendedCollection : FlexCollection<ExtendedItem> { };
于 2012-06-29T18:35:43.157 に答える
1

OK、C# イベントを試してみることにしました。このようにして、FlexCollection を所有するための参照を保持するために FlexItem は実際には必要ありません。対応するアクションを実行する場合、イベント、つまり「IAmBeingRemoved」と FlexCollection のコードを発生させるだけです。コード ロジックはさらに適切にカプセル化されます。パフォーマンスの問題やその他のジェネリックベースの驚きに遭遇しないことを願っています. :-) 誰かが自分の問題の良い解決策を見つけられるように投稿しています。

于 2012-06-28T12:45:48.057 に答える
0

あなたのコードの目的(使用法)が何であるかはわかりませんが、ジェネリックソリューションに基づいて何かをベースにしようとしていて、後でいくつかの型に関するジェネリックデータを「削除」しようとしているようです-これはC#では不可能です. ジェネリック アプローチを追求することを選択した場合は、常にジェネリック型のベースの情報を保持する必要があります。

    public class FlexItem<T> where T : FlexItem<T>, new()
    {
        public FlexCollection<T> ReferenceToParentCollection;
    }

    public class FlexCollection<T> where T : FlexItem<T>, new()
    {
        public void Add(T item)
        {
        }
    }

    public class BasicItem<T> : FlexItem<BasicItem<T>>
    {
        public int A;
    }

    public class BasicCollection<T> : FlexCollection<BasicItem<T>>
    {
    }

    public class ExtendedItem<T> : BasicItem<T>
    {
        public int B;
    }

    public class ExtendedCollection<T> : FlexCollection<T> where T: FlexItem<T>, new()
    {
    }
于 2015-02-05T16:24:51.147 に答える