2008-09-03 11 views
57

私はこの言葉を聞いたことがありますが、それが何を意味するのかはよく分かりません。C#でダブルディスパッチ?

いつこのテクニックを使用すればよいですか?どのように使用しますか?誰かが良いコードサンプルを提供できますか?

+0

現在のところ、これが最良の方法です。https://blogs.msdn.microsoft.com/curth/2008/11/15/c-dynamic-and-multiple-dispatch/ –

+0

ダブルディスパッチの素晴らしい例[リッチドメインモデルを構築するためのオブジェクト合成の適用](https://vimeo.com/195774910) – jsuddsjr

+0

[二重発送の注意](https://lostechies.com/derekgreer/2010/04/19/double-dispatch-is-a-code-smell/)。おそらく、より良いコード管理のためにそれを避けることができます。 –

答えて

54

ビジターパターンは、オブジェクト指向の方法で、二重派遣を行う方法です。

コンパイル時ではなく実行時に型に基づいて、指定された引数にどのメソッドを使用するかを選択する場合に便利です。

ダブルディスパッチは多重ディスパッチの特殊なケースです。

あなたがオブジェクト上で仮想メソッドを呼び出すと、それは実際のメソッドが呼び出されるので、単一発送は、単一のオブジェクトの種類に依存考えられています。

二重ディスパッチでは、オブジェクトの型とメソッドの単独引数の型の両方が考慮されます。これは、引数の型が実行時にコンパイル時に静的にではなく二重ディスパッチで決定される点を除いて、メソッドのオーバーロード解決と似ています。

複数ディスパッチでは、メソッドに複数の引数を渡すことができ、どの実装が使用されるかは、各引数の型によって異なります。型が評価される順序は、言語によって異なります。 LISPでは、最初から最後まで各タイプをチェックします。多重ディスパッチと

言語だけdelcarations機能と型パラメータを使用する一般的な方法のようではありませんされている一般的な機能の使用を作ります。 C#のに二重派遣を行うには

、あなたは唯一のオブジェクト引数を持つメソッドを宣言することができ、その後、特定の種類の特定のメソッド:

using System.Linq; 

class DoubleDispatch 
{ 
    public T Foo<T>(object arg) 
    { 
     var method = from m in GetType().GetMethods() 
        where m.Name == "Foo" 
         && m.GetParameters().Length==1 
         && arg.GetType().IsAssignableFrom 
              (m.GetParameters()[0].GetType()) 
         && m.ReturnType == typeof(T) 
        select m; 

     return (T) method.Single().Invoke(this,new object[]{arg});   
    } 

    public int Foo(int arg) { /* ... */ } 

    static void Test() 
    { 
     object x = 5; 
     Foo<int>(x); //should call Foo(int) via Foo<T>(object). 
    } 
}  
+2

LINQと反射の使い勝手 - 私は前に退屈なxxxInfoオブジェクトにそれを適用することは考えていませんでした。ありがとう! –

+24

これは素晴らしいアイデアだと私は確信していません。これは本当に二重ディスパッチを実装するわけではありません。タイプパラメタを指定するためには、コンパイル時に*型が何であるかを知る必要があります。すべての反射コードを気にせず、単にオブジェクトをキャストすることもできます。何か不足していますか? – ljs

+4

DoubleDispatchから派生したオブジェクトの実行時の型とメソッドの引数の両方でディスパッチするという点で、ダブルディスパッチを実装しています。この戻り型のリフレクションは、マカニズムをサブクラスに拡張するために使用されるので、サブクラスに "string Foo(string)"を追加すれば動作します。 –

11

まあちょっとみんなは、マークが掲示するコードが完全にイマイチこれまでに何が働いていないのですか?

このように調整され、完了しました。

class DoubleDispatch 
{ 
    public T Foo<T>(object arg) 
    { 
     var method = from m in GetType().GetMethods(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic) 
        where m.Name == "Foo" 
          && m.GetParameters().Length == 1 
          //&& arg.GetType().IsAssignableFrom 
          //     (m.GetParameters()[0].GetType()) 
          &&Type.GetType(m.GetParameters()[0].ParameterType.FullName).IsAssignableFrom(arg.GetType()) 
          && m.ReturnType == typeof(T) 
        select m; 


     return (T)method.Single().Invoke(this, new object[] { arg }); 
    } 

    public int Foo(int arg) 
    { 
     return 10; 
    } 

    public string Foo(string arg) 
    { 
     return 5.ToString(); 
    } 

    public static void Main(string[] args) 
    { 
     object x = 5; 
     DoubleDispatch dispatch = new DoubleDispatch(); 

     Console.WriteLine(dispatch.Foo<int>(x)); 


     Console.WriteLine(dispatch.Foo<string>(x.ToString())); 

     Console.ReadLine(); 
    } 
} 

おかげマークとダブルディスパッチャパターン上の素敵な説明のために他人

+0

私は同意します。このコードは完全であり、二重ディスパッチが本当に何であるかを示しています。マークの答えとこのコードの説明は完全に一致しています。 –

+0

書き込みしないでください "Console.WriteLine(dispatch.Foo (x));" パターンを活用するためには、よりダイナミックで便利な方法でしょうか? –

0

C#4は、(代わりに、コンパイル時の)実行時に関数呼び出しを解決する擬似タイプdynamicを紹介しています。 (つまり、式の実行時タイプが使用されます)。

class C { } 

static void Foo(C x) => Console.WriteLine(nameof(Foo)); 
static void Foo(object x) => Console.WriteLine(nameof(Object)); 

public static void Main(string[] args) 
{ 
    object x = new C(); 

    Foo((dynamic)x); // prints: "Foo" 
    Foo(x);   // prints: "Object" 
} 

一体型ではあるが、二重(またはマルチディスパッチ)は次のように簡略化することができます。 dynamicSystem.Objectとして扱われるので、上記の例ではvoid Foo(int x)を決して呼び出しません。

も注意dynamicを使用することにより、コードのこの部分を調べるために、コンパイラに静的解析器を防ぐこと。あなたは慎重に使用を考慮する必要がありますdynamic