C#で二重ディスパッチ?
-
09-06-2019 - |
質問
この用語を聞いたり読んだりしたことはありますが、その意味はよくわかりません。
このテクニックはいつ、どのように使用すればよいですか?誰か良いコードサンプルを提供してもらえませんか?
解決
ビジター パターンは、オブジェクト指向の方法で二重ディスパッチを行う方法です。
これは、コンパイル時ではなく実行時の型に基づいて、特定の引数に使用するメソッドを選択する場合に便利です。
二重派遣は特殊なケースです。 複数の発送.
オブジェクトの仮想メソッドを呼び出すと、どの実際のメソッドが呼び出されるかは単一オブジェクトの型に依存するため、単一ディスパッチとみなされます。
二重ディスパッチの場合、オブジェクトの型とメソッドの唯一の引数の型の両方が考慮されます。これはメソッドのオーバーロード解決に似ていますが、引数の型がコンパイル時に静的に決定されるのではなく、二重ディスパッチの実行時に決定される点が異なります。
マルチディスパッチでは、メソッドに複数の引数を渡すことができ、どの実装が使用されるかは各引数の型によって異なります。型が評価される順序は言語によって異なります。LISP では、各型を最初から最後までチェックします。
複数のディスパッチを備えた言語はジェネリック関数を利用します。ジェネリック関数は単なる関数のデリカレーションであり、型パラメーターを使用するジェネリック メソッドとは異なります。
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).
}
}
他のヒント
皆さん、Mark が投稿したコードは完全ではなく、そこにあるものはすべて機能しません。
ということで微調整して完成です。
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();
}
}
Double Dispatcher パターンについての素晴らしい説明をしてくれた Mark と他の人たちに感謝します
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"
}
を使用しても注意してください dynamic
コンパイラの静的アナライザーがコードのこの部分を検査しないようにします。したがって、の使用を慎重に検討する必要があります。 dynamic
.