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).
}
}
其他提示
嘿,伙计们,马克发布的代码并不完整,而且其中的任何内容都无法正常工作。
如此调整并完成。
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();
}
}
感谢 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
.
不隶属于 StackOverflow