2012-04-10 13 views
0

私はC#でアプリケーションを書いていますが、ジェネリックの実装については苦労しています。私はそうのような別の継承階層(モデルとビューモデル)でミラーリングされ、継承階層を持っている:継承階層のクラスの汎用メンバに型制約を課すことは可能でしょうか?

class A_Content { } 

class B_Content : A_Content 
{ 
    public string Bar; 
} 

class C_Content : A_Content 
{ 
    public string Foo; 
} 

class A { public A_Content content; } 
class B : A { } 
class C : A { } 

public class Test 
{ 
    IList<A> A_Collection = new List<A>(); 

    public Test() 
    { 
     B b = new B(); 
     C c = new C(); 

     b.content = new B_Content(); 
     c.content = new C_Content(); 

     A_Collection.Add(b); 
     A_Collection.Add(c); 
    } 
} 

これは十分に動作しますが、それをキャストする私を残す、content上の任意のタイプの制約を強制することはありません私はそれを使用するたびに適切な派生クラスです。私は、BオブジェクトがB_Contentのコンテンツしか持たないという制約を強制するようにコンパイラを同調させたいと思います。その時の私の最初のカットされた:

class A_Content { } 

class B_Content : A_Content 
{ 
    public string Bar; 
} 

class C_Content : A_Content 
{ 
    public string Foo; 
} 

class A { } 
class B : A { B_Content content; } 
class C : A { C_Content content; } 

public class Test 
{ 
    IList<A> A_Collection = new List<A>(); 

    public Test() 
    { 
     B b = new B(); 
     C c = new C(); 

     A_Collection.Add(b); 
     A_Collection.Add(c); 
    } 
} 

は、これはうまく動作しますが、私が持っているすべてはAのコレクションであるとき、私はcontentの共通の要素にアクセスできないことを意味します。私が本当にしたいことのようなものです:

abstract class A_Content { } 

class B_Content : A_Content 
{ 
    public string Bar; 
} 

class C_Content : A_Content 
{ 
    public string Foo; 
} 

abstract class A<T> { T content; } 
class B : A<B_Content> { } 
class C : A<C_Content> { } 

public class Test { 
    IList<A<A_Content>> A_Collection = new List<A<A_Content>>(); 

    public Test() 
    { 
     B b = new B(); 
     C c = new C(); 

     A_Collection.Add(b); 
     A_Collection.Add(c); 
    } 
} 

これは、しかし、Bは、暗黙のうちに、私は無駄に明示的なキャストを追加しようとしたA.に変換することができないことを訴えて、エラーを生成します。 2番目のモデルよりももっとエレガントに探している制約を表現する方法はありますか?

+0

私が質問を正しく理解していれば、あなたが探しているのはインターフェースです。各コレクションにアクセスできるリスト()が必要ですか? –

+0

@BillCarey:あなたはOOPの可能性を失う特定のタイプにキャストすることによって、このようにしてはいけません。私の答えをもう一度考えてみてください。 – Tigran

答えて

4

あなたが何をしているかは完全にはわかりません。 Aのすべてのインスタンスに、タイプがA_ContentContentというプロパティがあり、BのすべてがB_ContentのプロパティContentになるようにしようとしていますか?もしそうなら、あなたはそれをすることはできませんB/C/etcを持っています。 Aから継承します。 (とにかく、悪臭のない方法ではない)。 Aのシグニチャは、プロパティがA_Contentの有効な値を取得できる(と設定されている)必要があることを示しています。関数の戻り値の型または派生クラスのプロパティまたはフィールドの型を変更することはできません。 はジェネリックスを使用して基本的にクラスの使用方法までプロパティの型を遅らせることができますが、その構文は醜いものになります。たとえば、あなたはこの

を行うことができます:

public class A<TContent> where TContent : A_Content 
{ 
    public TContent Content { get; set; } 
} 

public class B<TContent> : A<TContent> where TContent : B_Content 
{ 
    // nothing here, as the property is already defined above in A 
} 

public class C<TContent> : A<TContent> where TContent : C_Content 
{ 
    // nothing here, as the property is already defined above in A 
} 

をしかし、これは二つのことを意味します

  • どこでもあなたがそう(あなたがTContentの実際の型を指定する必要がありますCABを使用するか、またはA_Content,B_Contentなど)。どちらが痛みです
  • A<B_Content>のようなことを止めることは何もありません(この場合、実際にはBは何もありません)。

要するに、私はあなたが戻ってきて、新しいデザインを考え出す必要があると思います。あなたはそれがA<A_Content>が含まれている必要がリストを言ったので、あなたの第二の例は、(Listに)飛ぶことはありません

ところで

理由があります。B<B_Content>はこれを満たさないため動作しません。これは典型的な分散問題であり、人ののロットを混乱させます。現実fooB<B_Content>ので、ランタイムwouldn以来

List<A<A_Content>> list = new List<A<A_Content>>(); 

list.Add(new B()); // this seems OK so far, right? 

A<A_Content> foo = list[0]; 

foo.content = new A_Content(): 

これは明らかに、壊れる:;しかし、(根本的な理由の実証であることを意図しています。このコードをコンパイルされません)このシナリオを考えますcontentB_Content(またはそれから継承するもの)のインスタンス以外のものと等しく設定します。, but the signature of the class means you should be able to assign anything that's A_Content`またはそれから継承します。

+0

+1質問を読む。私にとっては長すぎる。 –

3

あなたは、インターフェイスのメンバー(複数可)の明示的な実装に伴い、このためのインターフェイスを使用することができます。

abstract class A_Content {} 
class B_Content : A_Content {} 
class C_Content : A_Content {} 

interface IA 
{ 
    A_Content content { get; } 
} 

abstract class A<T> : IA 
    where T : A_Content 
{ 
    T content; 
    A_Content.content { get { return this.content; } } 
} 

class B : A<B_Content> {} 
class C : A<C_Content> {} 

を次にあなたがList<IA>BCオブジェクトの同種コレクションを保持することができます。

実際、C#4以降では、インターフェイスを汎用的かつ共変えることができます。

interface IA<out T> 
{ 
    T content { get; } 
} 

abstract class A<T> : IA<T> 
    where T : A_Content 
{ 
    T content { get; set; } 
} 

class B : A<B_Content> {} 
class C : A<C_Content> {} 

さて、BがまだA<A_Content>に変換することはできませんが、それはますので、IA<A_Content>に変換することができます(あなたはフィールドではなくプロパティを使用している限り)あなたは暗黙的インターフェイスを実装することができます均等なオブジェクトのコレクションを保持するのにList<IA<A_Content>>を使用できます。

1

確かBA<A_Content>に変換することができないので、まあ、コンパイラは、エラーを生成します。 A<A_Content>ないBのスーパークラスであるためです。 Bクラスの親クラスはA<B_Content>です。

私はあなたが鋳造に固執する必要があることを恐れています。 Aのリストがあるので、ここで必要です。 本当にキャスティングを避けたい場合(なぜあなたが望むのかわからない)、動的ディスパッチを試すことができます。

代わりList<A>List<dynamic>を作成してみてくださいすることができます。 少なくともC#4.0は必要です。

0

希望私は右のこの

IList<A>のようなコレクションを持つので

、あなたの意図をundertsoodあなたは異なる実装シナリオとAオブジェクトのコレクションを持っているしたいことを意味します。

このプロパティは、基本型のプロパティです。すなわち、にベース型が有することを意味する方法/プロパティを公開=>コンクリート実装を作るために有する子クラス状態及び行動プリミティブそう。

このような何か:

class A_Content { public virtual string Bar {get;set;} } 

class B_Content : A_Content 
{ 
    public override string Bar {get;set;}; 
} 

class C_Content : A_Content 
{ 
    public override string Bar {get;set}; 
} 

とどこかのコードで:

public Test() 
{ 
     B b = new B(); 
     C c = new C(); 

     A_Collection.Add(b); 
     A_Collection.Add(c); 

     //so 
     A_Collection[0].Bar // B::Bar 
     A_Collection[1].Bar //C::Bar 

} 

そして、あなたが実際のオブジェクトにキャストする必要はありません。シンプルなOOPアプローチ。

関連する問題