0

次のクラス構造があるとします。

class Base {}
class DerivedA: Base {}
class DerivedB: Base {}
class Container
{
    public List<Base> Items { get; }
}

アプリケーションがより多くの機能を開発するにつれて、派生オブジェクトのリストが時間の経過とともに大きくなります。私はやろうとしています:

Container container = ReturnsContainer();
foreach (var item in container.Items)
{
    doStuff(item);
}

ここで、doStuff(item) は派生型によってオーバーロードされます。

void doStuff(DerivedA) {}
void doStuff(DerivedB) {}

コンパイラが「'Base' から 'DerivedA' に変換できません」および「最適なオーバーロードされたメソッドの一致 doStuff(DerivedA) には無効な引数があります」と言うため、これは機能しません。私が思いつくことができる最善のアプローチは次のとおりです。

Container container = ReturnsContainer();
foreach (var item in container.Items)
{
    if (item is DerivedA)
    {
        doStuff((DerivedA)item);
    }
    else if (item is DerivedB)
    {
        doStuff((DerivedB)item);
    }
}

これをよりきれいにする方法について何か考えはありますか?私の派生型は時間の経過とともに成長するだけなので、これを機能させ続けるには、構造に戻ってこの long if 構造に追加する必要があります。

派生型の数が増えると、container.Items.OfType<> ソリューションはパフォーマンス ヒットの増加 (および不必要な) につながると思います。

これには一般的なデリゲートを使用する必要がありますか? 単純なポリモーフィズムであるべきだと思われるものに対して、過度に複雑なソリューションのように思えます。

編集: さらに明確にするために、基本型と派生型の階層が別の API レイヤーにあり、変更できないとしましょう (最終クラス)。このソリューションは、既存の継承構造で機能する必要があり、Base オブジェクトと Derived オブジェクトを拡張することはできません。ただし、API レイヤーは時間の経過とともに新しい派生クラスを成長させることができます。

4

4 に答える 4

3

どの関数を呼び出すかを決定するために実行時の型情報 (型の反映など) を使用する必要があるかdoStuff、基本クラスで宣言され、派生クラスで実装される抽象メソッド ( ) を使用することができます。多くの場合、リフレクションを使用した解決策を避けたいと考えていますが、関連するクラスを変更できない場合は、他に選択肢がありませんdynamic。他の回答で提案されているように使用することは、目的を達成するための非常に簡単な方法です。

たとえば、2 つのオーバーロードを実装します。

void doStuff(DerivedA a) {
  ...
}

void doStuff(DerivedB b) {
  ...
}

そして、ループからそれらを呼び出します:

foreach (dynamic item in container.Items)
  doStuff(item);
于 2012-11-28T15:15:56.353 に答える
2

ここにはいくつかのオプションがありますが、最終doStuuf的には、将来新しい派生型を追加する場合に何をすべきかを知るにはどうすればよいでしょうか?

したがって、1 つのオプションは、Container実際に一度に 1 つのタイプしか含まない場合は、ジェネリックにすることができます。

class Container<T> where T: Base
{
    public List<T> Items { get; }
}

Itemsこれを反復すると、それがまたはのリストであることがわかりDerivedAますDerivedB

Container<DerivedA> container = ReturnsContainerOfDerivedA();
foreach (var item in container.Items)
{
    doStuff(item); // item is definately DerivedA
}

これは、複数のdoStuffメソッドがあることを意味します

public void doStuff(DerivedA item){...}
public void doStuff(DerivedB item){...}

または、より適切な場合は、ベースを取る 1 を持つことができます

public void doStuff(Base item){...}

これ単純なポリモヒズムの利点です。


2 番目のオプションは、 C#3.5 を使用している場合にdoStuffa を取得するように定義することです。dynamic

public void doSomething(dynamic item){..}

しかし、私は個人的にこのオプションを避けたいと思います!

于 2012-11-28T15:18:08.823 に答える
1

オブジェクトのユーザーは、それがBaseどのタイプのオブジェクトであるかを知る必要がありBaseます。Baseこのような場合、おそらく抽象化する必要があることは明らかでありdoStuff、オブジェクトから必要な動作を定義する抽象メソッドを持つ必要がありBaseます。(Base抽象化がオプションでない場合は、オーバーライドすることを意図した本体を持たない仮想メソッドを定義するか、すべての派生オブジェクトにインターフェイスを実装させることができます。)

それが完了すると、各派生オブジェクトは定義されたメソッドをオーバーライドして、各タイプ間で異なる動作を提供できます。

Containerオブジェクトを 以外のものにキャスト/変換する必要がないことを完了するとBase、それぞれが単一のメソッドBaseに必要なすべての情報をすでに持っています。doStuff

于 2012-11-28T15:24:11.887 に答える
1

最も簡単な解決策 ( Base/DerivedAなどを変更せずに) は、オブジェクトをdynamic次のようにキャストすることです。

foreach (dynamic item in container.Items)
{
    doStuff(item);
}

そのため、実際に呼び出すメソッドは実行時に決定されます。

于 2012-11-28T15:18:44.897 に答える