4

基本クラス BC と 2 つの派生クラス DC_A および DC_B を持つ次のクラス構造を想定してみましょう。さらに、タイプ BC のパラメーターを持つメソッド goo() を持つクラス XY と他のメソッドがあります

// base class
public class BC
{
    public virtual void foo();
}

// derived class A
public class DC_A : BC
{
    public override void  foo() {}
}

// derived class B
public class DC_B : BC
{
    public override void  foo() {}
}


public class XY
{
    public void goo(BC o)
    {
        // perfectly fine using polymorphism; no Ifs' no casting, OOP at its best ;-)
        o.foo();

        // but what to do here?
        if ( (o as DC_A) != null )
        {
            doX(o as DC_A);
        }
        else if ((o as DC_B) != null)
        {
            doY(o as DC_B);
        }
    }

    private void doX(DC_A o) {}
    private void doY(DC_B o) {}
}

In 'ポリモーフィズムを使用して値オブジェクトを渡すのは悪い習慣ですか? ' Visitor パターンが提案されます。

If カスケードとキャストの問題は、抽象基本クラスに移動されます (排除されません)。

if を完全に回避するためのより良い解決策はありますか?

(この例では) 機能を doX/doY からクラス DC_A/DC_B に移動するオプションはありません。

あなたのアドバイスは大歓迎です。

編集:この質問の背景は、測定タイプ、テスト制限などのコレクション(私のDC_A、DC_Bクラス)を持つTestSpecのような異なるサブエンティティで構成される「テストルール」を管理するフォームを備えたC#/ WinFormsアプリケーションです。すべて EntityBase から派生 (=上記の BC) フォームは、サブエンティティが変更されたというイベントをコントローラーに送信します。単純化された PropertiesChanged(EntityBase o) コントローラーは、モデル クラスの対応するメソッド (上記のクラス XY のメソッド goo) を呼び出します。このメソッドは、変更されたサブ エンティティ テスト制限を保持するだけでなく、たとえば新しいテスト リミット リビジョン オブジェクト、テスト スペック リビジョンの増加など。

しかし、この特別なケースにより、ポリモーフィズムについてのより一般的な、または「哲学的」な質問に私は導かれました ;-)

4

3 に答える 3

5

.NET 4 を使用している場合は、オーバーロードと動的型の可能性があります。おそらくそれが代替手段です。

class Program
{
    static void Main(string[] args)
    {
        DC_A dca = new DC_A();
        DC_B dcb = new DC_B();
        XY xy = new XY();
        xy.goo(dca);
        xy.goo(dcb);
    }
}

// base class
public abstract class BC
{
    public abstract void foo();
}

// derived class A
public class DC_A : BC
{
    public override void foo() { }
}

// derived class B
public class DC_B : BC
{
    public override void foo() { }
}


public class XY
{
    //public void goo<T>(T o) where T : BC
    //{
    //    //dynamic dyn = Convert.ChangeType(o, o.GetType());
    //    //dynamic dyn = o;
    //    //gooi(dyn);
    //    gooi((dynamic)o);
    //}

    // http://smellegantcode.wordpress.com/2008/11/04/dynamic-typing-in-c-4-and-duck-generics/
    public void goo<T>(T o) where T : BC
    {
        gooi((dynamic)o);
    }

    private void gooi(DC_A o)
    {
        o.foo();
        doX(o);
    }

    private void gooi(DC_B o)
    {
        o.foo();
        doY(o);
    }

    private void gooi(BC o)
    {
        o.foo();
    }

    private void doX(DC_A o) { }
    private void doY(DC_B o) { }
}
于 2012-12-05T11:20:05.800 に答える
3

私のおすすめ:

  1. doX()メソッドとdoY()メソッドを公開して、DC_AとDC_Bの実装から呼び出すことができるようにします。
  2. 抽象Do()メソッドをBCに追加します。
  3. DC_AにDo()を実装して、公開されたdoX()メソッドを呼び出します。
  4. DC_BにDo()を実装して、公開されたdoY()メソッドを呼び出します。
  5. goo()内で、o.Do()を呼び出すだけです。

doX()とdoY()を公開する代わりに、それらが呼び出すDC_AとDC_Bにデリゲートを渡すことで、物事をさらに切り離すことができます。それは使用パターンに大きく依存します。

ただし、これは、派生クラスのDo()実装に何らかの値を追加できる場合にのみ非常に役立ちます。Do()が行うのがdoX()またはdoY()を呼び出すだけの場合、この方法で行うことによるメリットは実際には得られません。

だから私はここでキャスティングをするだけだと言っている他のポスターに同意するつもりです。

于 2012-12-05T09:28:11.197 に答える
2

いくつかのことを修正すれば、あまり心配する必要はないと思います。

  • 現在、必要以上にキャストしています
  • これらifの s の 1 つだけが true になる可能性があるため、else

その後、次のようになります。

    // perfectly fine using polymorphism; no Ifs' no casting, OOP at its best ;-)
    o.foo();

    var dca = o as DC_A;
    if (dca != null)
    {
        doX(dca);
    }

    var dcb = o as DC_B;
    if (dcb != null)
    {
        doY(dcb);
    }

私はそれがうまく見えると思います。

さらに進みたい場合は、doXandの実装を変更してdoY、渡された場合にすぐに返されるようにすることができnullます。それならあなたはただ言うことができます

doX(o as DC_A);
doY(o as DC_B);
于 2012-12-05T09:33:59.510 に答える