2013-12-20 15 views
10

私はいくつかの基本クラスとそれから派生するものを含むクラスライブラリを持っています。このクラスライブラリでは、多形性を利用して、自分がやりたいことをやっています。今では、消費するアプリケーションで、子クラスの実行時の型に基づいていくつかのコードの動作を変更したいと思います。だから、次のことを想定します。拡張メソッドによる多型?

public class Base { } 
public class Child1 : Base { } 
public class Child2 : Base { } 

を今、私は(次のクラスのすべてが消費するアプリケーションであり、クラス・ライブラリで参照することができないことに注意)次のように何かをしたい消費するアプリケーションで:

public interface IMyInterface1 { } 
public interface IMyInterface2 { } 
public static class Extensions 
{ 
    public static void DoSomething(this Base myObj, Object dependency) 
    { 

    } 

    public static void DoSomething(this Child1 myObj, Object dependency) 
    { 
     IMyInterface1 myInterface = dependency as IMyInterface1; 
     if (myInterface != null) 
     { 
      //Do some Child1 specific logic here 
     } 
    } 

    public static void DoSomething(this Child2 myObj, Object dependency) 
    { 
     IMyInterface2 myInterface = dependency as IMyInterface2; 
     if (myInterface != null) 
     { 
      //Do some Child2 specific logic here 
     } 
    } 
} 

UPDATE:

これは動作しません。これは、常に、基本クラスの拡張メソッドを呼び出します。私はこれを行うことができ、実行時の型を明示的にチェックする必要を避けることができる他の方法がありますか?その理由は、Baseから派生したクラスが追加され、対応する拡張メソッドが他の外部アセンブリから来る可能性があるからです。

ありがとうございます。

+5

は、なぜあなたは、その後、必要に応じて何が悪かったのかの詳細を聞いて、最初にそれを試してみませんか? –

+0

これでOKです。 – Kassem

+1

これは動作しません。拡張メソッドが静的にディスパッチされます。 – SLaks

答えて

7

@SLaksは、すでにあなたが(でもdynamicタイプの)拡張メソッドとしてメソッドを呼び出すことはできません述べたように、これは失敗しますが...あなたは、しかし、そうdynamicタイプ

で静的メソッドを呼び出すことができます

Base base1 = new Child1(); 
(base1 as dynamic).DoSomething(); 

これは、私はちょうど今、同じものを探していた

Base base1 = new Child1(); 
Extensions.DoSomething(base1 as dynamic); 
+0

これは実際に働いています!非常にスマートな私は言わなければならない!結論として、私は静的メソッドでそれを実現し、それをあなたが推奨するように呼び出すことができますが、インスタンス上で呼び出される拡張メソッドを使用して行うことはできません。 – Kassem

+0

@カッセムはい、それは私がここで働くために得ることができる唯一の方法です。 – qujck

+1

それは本当に私を驚かせます。 AFAIKでは、拡張メソッド(動的では機能しません)は、コンパイラによって(動作する)同じ静的メソッド呼び出しに変換されます。それぞれのケースでILを見るのは興味深いでしょう(しかし、今はコンパイラにアクセスできません)。誰がこの理由が説明できますか? – Baldrick

4

いいえ、それは動作しません。

拡張メソッドは、オーバーロードの解決と同じメカニズムを使用して静的にディスパッチされます。

コンパイル時の型がBaseの場合、コンパイラは実行時の型に関係なく、常にベース拡張メソッドを呼び出します。

代わりに、基本拡張メソッドで実行時の型をチェックし、適切な他の拡張メソッドを呼び出すことができます。

+1

'Base'から派生したより多くのクラスが追加され、対応する拡張メソッドが他の外部アセンブリから来る可能性があるため、実行時の型のチェックを避けようとしています。 – Kassem

1

動作します。

あなたはこのようなあなたの拡張クラスに1つのより多くのメソッドを追加することができます。

public static void DoSomething(this Base myObj, Object dependency) 
{  
    if(myObj.IsSubclassOf(Base)) 
    { 
     // A derived class, call appropriate extension method. 
     DoSomething(myObj as dynamic, dependency); 
    } 
    else 
    { 
     // The object is Base class so handle it. 
    } 
} 

をあなたは、基本クラスが抽象的(あるいは野生で使用されることはありません)の場合であれば/それ以外はチェックする必要はありません

public static void DoSomething(this Base myObj, Object dependency) 
{ 
    DoSomething(myObj as dynamic, dependency); 
} 

[編集]実際には、すべての派生オブジェクトのサポートを実装していないので、これはあなたのケースでは機能しません(したがって、無限再帰を得ることができます)。あなたは再帰をチェックするために何かを渡すことができると思いますが、与えられた答えは最も簡単です。私はもっ​​と多くのアイデアを発するかもしれないので、ここに残しておきます。

1

以下は、拡張方法と多型を模倣する方法を示す最小限の例です。

void Main() 
{ 
    var elements = new Base[]{ 
     new Base(){ Name = "Base instance"}, 
     new D1(){ Name = "D1 instance"}, 
     new D2(){ Name = "D2 instance"}, 
     new D3(){ Name = "D3 instance"} 

    }; 

    foreach(Base x in elements){ 
     x.Process(); 
    } 
} 

public class Base{ 
    public string Name; 
} 
public class D1 : Base {} 
public class D2 : Base {} 
public class D3 : Base {} 


public static class Exts{ 

    public static void Process(this Base obj){ 
     if(obj.GetType() == typeof(Base)) Process<Base>(obj); //prevent infinite recursion for Base instances 
     else Process((dynamic) obj); 
    } 

    private static void Process<T>(this T obj) where T: Base 
    { 
     Console.WriteLine("Base/Default: {0}", obj.Name); 
    } 

    public static void Process(this D1 obj){ 
     Console.WriteLine("D1: {0}", obj.Name); 
    } 

    public static void Process(this D2 obj){ 
     Console.WriteLine("D2: {0}", obj.Name); 
    } 
} 

出力:

Base/Default: Base instance 
    D1: D1 instance 
    D2: D2 instance 
    Base/Default: D3 instance 
0

キーワード「ダイナミック」(.NETの以前のバージョン)を使用することができない場合は、あなたが同じことを達成するためにリフレクションを使用することができます。代わりに

Base base1 = new Child1(); 
Extensions.DoSomething(base1 as dynamic); 

次のように記述することができます:

Base base1 = new Child1(); 

MethodInfo method = typeof(Extensions).GetMethod("DoSomething", new System.Type[] { base1.GetType() }); 
if (method) { 
    method.Invoke(new object[] { base1 }); 
}