2008-09-03 47 views
57

我聽說過/讀過這個詞,但不太明白它的含義。C#中的雙重調度?

什麼時候應該使用這種技術,我將如何使用它?任何人都可以提供一個好的代碼示例?

+0

目前這是做到這一點的最佳方式:https://blogs.msdn.microsoft.com/curth/2008/11/15/c-dynamic-and-multiple-dispatch/ – 2016-10-11 23:20:05

+0

[應用對象組合構建豐富的域模型](https://vimeo.com/195774910)。 – jsuddsjr 2016-12-16 19:06:27

+0

[小心雙重派遣](https://lostechies.com/derekgreer/2010/04/19/double-dispatch-is-a-code-smell/)。可能你可以避免它,以更好的代碼維護。 – 2017-01-19 15:40:02

回答

54

訪問者模式是一種以面向對象的方式進行雙重調度的方式。

當您想根據運行時的類型而不是編譯時選擇給定參數使用哪種方法時,它非常有用。

雙派遣是多派遣的特例。

當你在一個對象上調用一個虛擬方法時,這被認爲是單調度的,因爲調用的實際方法取決於單個對象的類型。

對於雙重調度,對象的類型和方法唯一參數的類型都被考慮在內。這就像方法重載解析一樣,除了參數類型是在運行時以雙分派而不是在編譯時靜態確定的。

在多次調度中,一個方法可以有多個參數傳遞給它,並且使用哪個實現取決於每個參數的類型。類型的評估順序取決於語言。在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). 
    } 
}  
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(); 
    } 
} 

感謝Mark和他人很好解釋雙分派器圖案

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" 
} 

不過要小心整數類型。由於dynamic被視爲System.Object,因此在上例中永遠不會調用void Foo(int x)

還要注意,通過使用dynamic可以防止編譯器使用靜態分析器來檢查這部分代碼。您應該仔細考慮使用dynamic