2016-04-01 8 views
1

私は、繰り返し実行して各項目に対していくつかのコードを実行する必要があるIEnumerableを持っている状況があります。サードパーティクラスへの多態的な拡張

このコードは実際の項目の種類に依存しますが、私が探しているのは、条件付きでなくてもこれを行うきれいな方法です。新しいハンドラを作成して既存のコードを変更する必要はありません。私はを作成しようとした

void Execute(IEnumerable<BaseObject> list) 
{ 
    foreach (var item in list) 
     item.DoWork(); 
} 

public abstract class BaseObject{ } 

public class Derived1: BaseObject { } 

public class Derived2 : BaseObject { } 

と私の地元のコードは次のようなものを行います。

は、私がサードパーティのライブラリは、以下のコードが含まれている例があり、これを説明するために、拡張メソッド
これを行うには:

public static class Extensions 
{ 
    public static int DoWork(this BaseObject obj) { return 0; } 

    public static int DoWork(this Derived1 obj) { return 1; } 

    public static int DoWork(this Derived2 obj) { return 2; } 
} 

列挙の各項目がBaseObject型であるため、明らかに機能しないため、0を返す拡張メソッドが常に呼び出されます。

void Execute(IEnumerable<BaseObject> list) 
{ 
    foreach (var item in list) 
    { 
     var local = item as IMyInterface; 
     local?.DoWork(); 
    } 
} 
新しいインターフェイスから

public class MyDerived1: Derived1, IMyInterface 
{ 
    public int DoWork(){return 1;} 
} 

public class MyDerived2: Derived2, IMyInterface 
{ 
    public int DoWork(){return 2;} 
} 

public interface IMyInterface 
{ 
    int DoWork(); 
} 

が、私は反復可能性があり、キャスト:

私はインターフェイスを実装する各派生型のすべてののための新しい派生型を作成するオプションを持っています

しかし、これには派生した型の構築が必要であり、どの型を構築するかの決定には、条件付きの文が必要です。新しいタイプが追加されました。

明らかにここでは工場パターンを使用していますが、私がこれまでに持っている最もクリーンなソリューションです。

、誰もがそれだろうなアプローチを提案することができます...

をコマンドパターンがソリューションを提供していますが、実行するコマンドは、派生型の種類に依存しているように、条件がまだこのコマンドを構築するために必要とされます新しい派生型がミックスに追加されたときに変更する必要がある条件を完全に排除しますか?

+0

あなたはあなたの工場は、任意の条件文を必要とせずに右の派生型を構築できるようになるリフレクションを使用して、起動時に辞書を構築することができ、そしてマップが自動的に修正を必要とせずに追加された新しい派生型が含まれるであろう。 – Baldrick

+0

@Baldrickに感謝します。これはこの記事の答えで取られたアプローチです。http://stackoverflow.com/questions/27724130/simulate-inheritance-by-extension正直言って私は反射を避けるために探していた。 – MikeW

+0

私はそれが可能であるかどうかはわかりません。 (a)オーバーライドされたメソッドへのオブジェクト自体の仮想メソッド呼び出し(ただし、サードパーティ製であり、変更することはできません)、または(b )を反映し、次に条件文を使用します。あなたの状況に別の方法があるかどうかはわかりません。 – Baldrick

答えて

0

一つの潜在的な解決策は、構成フレームワークを使用します。私は私の例としてMEFを選択しました。

public interface IBaseObjectWorker 
{ 
    int DoWork(BaseObject obj); 
} 

[Export(contractType: typeof(IBaseObjectWorker), contractName: "UnitTestProject1.Derived1")] 
public class DerivedObject1Worker : IBaseObjectWorker 
{ 
    public int DoWork(BaseObject obj) 
    { 
     return 1; 
    } 
} 

[Export(contractType: typeof(IBaseObjectWorker), contractName: "UnitTestProject1.Derived2")] 
public class DerivedObject2Worker : IBaseObjectWorker 
{ 
    public int DoWork(BaseObject obj) 
    { 
     return 2; 
    } 
} 

は、あなたが得ることができる:あなたがする必要がどのような

は共通のインタフェースを実装するハンドラを作成され、それぞれが派生型の名前と一致するコントラクト名を持つ組成物容器内に含まれています新しい派生型を処理するためにこれを拡張すると、新しいハンドラ型の作成が簡単になります。コンポジションコンテナにアセンブリのすべての型が設定されている場合、新しいハンドラは作成されるとすぐにそこに存在し、現在のコードに変更を加えずに新しい派生型の処理を開始します。したがって、オープン・クローズの原則に従う。

これはファクトリパターンの実装の一種ですが、ファクトリの実装について心配する必要はありません。

DIContainerはMEFコンテナの周りに単一のパターンラッパーを実装しているわけではありません。これはアプリの起動時に設定され、GetObject(文字列名)は単にGetExportedValue(string contractName)を呼び出します。

0

多型の自然な機能を使用できます。

class Class1 
{ 
    public virtual void DoThis() 
    { 
     Console.WriteLine("Class one"); 
    } 
} 

class Class2 : Class1 
{ 
    public override void DoThis() 
    { 
     Console.WriteLine("Class two"); 
    } 
} 

class Class3 : Class1 // or Class2 
{ 
    public override void DoThis() 
    { 
     Console.WriteLine("Class three"); 
    } 
} 


    private static void Main(string[] args) 
    { 
     Class1 class1 = new Class1(); 
     Class2 class2 = new Class2(); 
     Class3 class3 = new Class3(); 
     class1.DoThis(); 
     class2.DoThis(); 
     class3.DoThis(); 
    } 

など。

private static void Main(string[] args) 
{ 
    Class1 class1 = new Class1(); 
    Class2 class2 = new Class2(); 
    Class3 class3 = new Class3(); 
    class1.DoThis(); 
    class2.DoThis(); 
    class3.DoThis(); 
} 

が故に、それが何であるか、オブジェクトタイプのない取り扱いがないように必要があるだろう、それは当然、クラスのオーバーライドされたメソッドに下落するだろう。

派生クラスが追加されるにつれて乱雑になり、コードのどこかに条件文が必要になる可能性が高いオブジェクトタイプについての問い合わせが必要です。派生クラスごとにオーバーライドされたメソッドを使用することで、自動的にプログラムを実行します。

私の考え方によれば、OOPに関連する言語のファセットを繰り返すことによって、負荷が不必要に増加します。時には、よりシンプルなアプローチがより良く、よりエレガントになることがあります。

+0

ここでは、「第三者の図書館コード」を変更することができます。 – Maarten

+0

@Maartenああそうです。私はあなたのダイナミックなソリューションが好きでした。私はそれがコメントよりも簡単だと思います。 –

4

dynamicを使用できます。通常、私はそれを好きではありませんが、この場合は良い選択肢と思われます。

public static class Worker 
{ 
    public static int DoWork(BaseObject obj) { return 0; } 
    public static int DoWork(Derived1 obj) { return 1; } 
    public static int DoWork(Derived2 obj) { return 2; } 
} 

void Execute(IEnumerable<BaseObject> list) { 
    foreach (dynamic item in list) { 
     Worker.DoWork(item); // Method resolution done at run-time 
    } 
} 
+1

効果的にはまだ 'リフレクション'を使用していますが、少なくとも醜いコードを必要とせずに隠されています。私の+1。 – Baldrick

+0

OPがクラスを変更できないと思いますが、これは良い解決策です+1 –

関連する問題