2012-02-02 10 views
5

私はC#4.0のジェネリックと忙しかった、そして今、私は基本的にこのような何かをしたい:C# - ジェネリックコレクションをキャストする方法はありますか?

public abstract class GenericTree<T> : Tree 
    where T : Fruit 
{ 
    public Tree(IFruitCollection<T> fruits) 
     : base(fruits) { } 
} 

基本Treeクラスは次のようになります。ここでは

public abstract class Tree 
{ 
    private IFruitCollection<Fruit> fruits; 

    public IFruitCollection<Fruit> GetFruits 
    { 
     get { return fruits; } 
    } 

    public Tree(IFruitCollection<Fruit> fruits) 
    { 
     this.fruits = fruits; 
    } 
} 

は私の最初の問題です。 GenericTreeのコンストラクタは、ジェネリックコレクションをフルコレクションにキャストできません。私もGenericTreeの実装を持っています:

public class AppleTree : GenericTree<Apple> 
{ 
    public AppleTree() 
     : base(new FruitCollection<Apple>) { } 
} 

ここに私の2番目の問題があります。 myAppleTree.GetFruits.Add(...)を使用してAppleTreeのインスタンスに果物を追加するとき、私はリンゴだけに限定されません。私はあらゆる種類の果物を加えることが許されています。私はこれを望んでいない。

私はGenericTreeにこれを追加することで問題を解決しようとした:

new public IFruitCollection<T> GetFruits 
{ 
    get { return base.GetFruits as IFruitCollection<T>; } 
} 

しかし、これはどちらかのことはできません。それはどういうわけか常にnullを返します。私の最初の問題が解決されると、これが解決される可能性があります。

IFruitCollectionインターフェイスは次のようになります。

public interface IFruitCollection<T> : ICollection<T> 
    where T : Fruit { ... } 

そしてFruitCollectionクラスは、コレクションクラスの簡単な実装です。 もちろん、AppleクラスはFruitクラスを拡張しています。

解決策は、IFruitCollectionインターフェイスを共分散と反共分散の両方に対応させることです。しかし、私はこれをどのように達成するのですか? "in"または "out"パラメータキーワードは、ICollectionインターフェイスでは許可されていないため、使用できません。

ありがとうございました!あなたのコメントを受けて

+0

これは簡単に一つにツリーとGenericTreeクラスをマージすることで解決することができることに注意してください。しかし、これは私の問題の一例に過ぎないことを忘れないでください。自分のアプリケーションはもう少し複雑です。 –

+0

あなたが示唆するように、解決策はTreeを汎用的にすることです。また、これは不可能であることを暗示します。何故なの? – phoog

+0

ある時点で、私は木のリストが必要です。ツリーのリストは、ツリーのインスタンスを追加できないため、それを行いません。 Tree とツリーの間に暗黙的な参照がないことは言うまでもないでしょう。 –

答えて

1

phoogの回避策を試していたのですが、解決策が見つかったと思います。ありがとう、プッシュ!

まず、私はGenericTreeに自分のFruitCollectionを与えませんでした。 Mostyは基底クラスがしばしば自分のコレクションをループするので、例えば果物を更新するためです。これは、基本コレクションではなくGenericTreeのコレクションに追加されたため、更新されない果実になりました。

しかし、結局私はそれらのコレクションをキャストすることも決してうまくいかないことに気付きました。

今度は、コンポーネントをFruitCollectionのベースに自動的に追加したり削除したりする別のFruitCollectionクラスを作成しました。このソリューションは私にとって素晴らしい作品です!

public FruitCollection<T, U> : FruitCollection<T> 
    where T : U 
    where U : Fruit 
{ 
    private FruitCollection<U> baseCollection; 

    public FruitCollection(FruitCollection<U> baseCollection) 
     : base() 
    { 
     this.baseCollection = baseCollection; 

     // here I added code that throws events whenever the collection is changed 
     // I used those events to add/remove fruit to the base collection, and vice versa 
     // see the link below. 
     ... 
    } 

    ... 
} 

public class GenericTree<T>: Tree 
    where T : Fruit 
{ 
    private FruitCollection<T> fruits; 

    // use the new keyword to hide the base collection 
    new public FruiCollection<T> Fruits 
    { 
     get { return fruits; } 
    } 

    public GenericTree() 
     : base() 
    { 
     // after hiding the base collection, use base.Fruits to get it 
     fruits = new FruitCollection<T, Fruit>(base.Fruits); 
    } 
} 

これは、私を助け:How to handle add to list event?

2

:いくつかの時点で

、私は木のリストを必要としています。 Tree<Fruit>のリストは、Tree<Banana>のインスタンスを追加することができないため、それを行いません。もちろん、Tree<Fruit>Tree<Banana>の間に暗黙的な参照がないと言うよりもそうです。

基本的な問題は、(間接的に)異なるタイプの類似のオブジェクトを含むコレクション(ツリーのリスト)を持ちたいということです。 (コレクションでもある、Fruit)からそのコレクションを明確にするため、それをOrchardとしましょう。

オーチャード - (含まれている) - >ツリー - (含まれている) - >フルーツ

あなたは非ジェネリックTreeを作る場合は、Orchardの要素はすべて、そのタイプのものであってもよいです。しかし、あなたが気づいたように、これは木がタイプセーフではないという問題に終わることを意味し、リンゴの木にバナナを置くことができます。 Tree実装では、実行時の型チェックでその問題を解決する必要があります。

また、汎用Tree<T> where T : Fruitクラスを作成することができるので、ツリーに含まれるサブクラスFruitに対して型の安全性があります。つまり、Orchardには異なるタイプのオブジェクトが含まれ、実行時の型チェックの必要があります。

(あなたは種類ごとに別々のアクセサ宣言することで、静的な型の安全性とオーチャードを作ることができる:

class Tree { } 
class Tree<T> : Tree { } 
class Trees : IEnumerable<Tree> 
{ 
    Tree<Banana> _bananaTree; 
    Tree<Apple> _appleTree; 
    //...etc. 

    Tree<Banana> GetBananaTree() { return _bananaTree; } 
    Tree<Apple> GetBananaTree() { return _appleTree; } 
    //...etc. 

    public IEnumerator<Tree> GetEnumerator() 
    { 
     yield return _bananaTree; 
     yield return _appleTree; 
     //...etc. 
    } 
} 

をしかし、それはあなたが望むものはおそらくないので、どこかキャストを持っている必要があります。 )

私は基本的なアプローチのために、このような何かを示唆している場合には、あなたではなくTreeよりもOrchardでキャストを持っているだろうと仮定します。

IDictionary<Type, object> orchard = new Dictionary<Type, object>(); 

//to retrieve the tree 
Tree<Banana> bananaTree = (Tree<Banana>)orchard[typeof(Banana)]; 

(もちろん、あなたがTree、またはICollection、またはIEnumerableのように、objectよりも多くの特定のタイプを使用することができます。)

私はさらに行くとあまりを提供することができOrchardクラスでそのロジックをカプセル化しますアクセサでキャストを行うことで冗長な構文:

var bananaTree = orchard.GetTree<Banana>(); 

public class Orchard 
{ 
    private IDictionary<Type, object> _trees; 

    //... 

    public Tree<T> GetTree<T>() 
    { 
     return (Tree<T>)_trees[typeof(T)]; 
    } 
} 

最終的に、これは、界面の共変型パラメータと反動型パラメータを出力位置と入力位置にそれぞれ制限する必要がある理由の素晴らしい例です。入力と出力の両方で使用される型の場合、実行時の型チェックが必要になります。

+0

ご返信ありがとうございます。昨日、私は非ジェネリックTreeクラスを取り除く必要があることに気付きました。そして、あなたは私のためにそれを実際に確認しました。あなたのオーチャードクラスは素晴らしい回避策のようです。私はこれが私の「本当の」アプリケーションには当てはまりませんが、明日はそれを試してみます。 –

+0

あなたの回避策はうまくいきましたが、あなたの使用中に別の回避策が見つかりました。私自身の答えを見てください。あなたの助けをありがとう! –

+0

@Rudeyそれは面白い解決策です。たくさんのオブジェクトがないとうまくいくでしょう。コレクションに何百万ものオブジェクトが含まれているプロジェクトがあります。異なるタイプの参照用の* 2つのコレクションを持つストレージオーバーヘッドは、そこでは受け入れられません。 – phoog

関連する問題