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_ContentB オブジェクトにはコンテンツしかないという制約を強制するようにコンパイラーを説得したいと思います。私の最初のカットは次のとおりです。

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);
    }
}

これはうまく機能しますcontentが、 のコレクションしか持っていない場合の共通要素にアクセスできないことを意味しますA。私が本当にやりたいことは次のようなものです:

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 番目のモデルよりもエレガントに表現する方法はありますか?

4

4 に答える 4

4

あなたが何を求めているのかは完全には明らかではありません。のすべてのインスタンスが であるプロパティをA持ち、すべてのインスタンスが であるプロパティを持つようにしようとしていますか? もしそうなら、あなたはそれをすることができず、/ / etc. から継承します。(とにかく、臭いのない方法ではありません)。のシグネチャは、プロパティが の任意の有効な値を取得 (および、おそらく設定) できる必要があることを示しています。関数の戻り値の型、または派生クラスのプロパティまたはフィールドの型を変更することはできません。あなたができるContentA_ContentBContentB_ContentBCAAContentA_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
}

しかし、これは次の 2 つのことを意味します。

  • AB、または(so 、など)Cの実際のタイプを指定する必要があります。どっちが痛いTContentA_ContentB_Content
  • 次のようなことを行うことを妨げるものはまったくありません(実際、クラスに何も追加していないため、この場合はA<B_Content>本質的にそうです)。B

要するに、立ち止まってパントし、新しいデザインを考え出す必要があると思います。

ところで

2 番目の例が ( を使用して) 飛ばない理由Listは、リストに を含める必要があることを伝えたからですA<A_Content>。それB<B_Content>を満たさないので、うまくいきません。これは典型的な差異の質問であり、多くの人を混乱させます。ただし、次のシナリオを考えてみましょう (このコードはコンパイルされません。根本的な理由を示すことを目的としています)。

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():

foo 実際には aであるため、これは明らかに壊れます。そのため、ランタイムでは、 A_Content`のインスタンス(またはそれから継承するもの) またはそれから継承する以外のものに等しい値をB<B_Content>設定することはできません。contentB_Content, but the signature of the class means you should be able to assign anything that's

于 2012-04-10T20:37:04.107 に答える
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>、同種のBandCオブジェクトのコレクションを保持する を作成できます。

実際、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>>て同種のオブジェクトのコレクションを保持できます。

于 2012-04-10T20:41:41.857 に答える
1

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

残念ながら、キャストに固執する必要があります。のリストがあるため、ここで必要ですA。本当にキャストを避けたい場合 (なぜそうしたいのかわかりません)、動的ディスパッチを試すことができます。

List<dynamic>の代わりに を作成してみてくださいList<A>。ただし、少なくとも C# 4.0 が必要です。

于 2012-04-10T20:42:32.110 に答える
0

私はあなたの意図を正しく理解したと思いますので、

このようなコレクションを持っている

IList<A>さまざまな実装シナリオ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 アプローチ。

于 2012-04-10T20:43:23.843 に答える