2013-05-01 66 views
8

我有一個應用程序正在對一系列元素執行操作,操作的確切性質取決於正在操作的元素的類型。出於封裝的原因,元素不適合執行操作;這意味着它不能是元素類型的虛擬方法,因此「標準」多態不起作用。我提出了與此相關的previous question,並被告知這被稱爲訪問者模式。在C#中使用'dynamic'來實現訪問者模式

我以前總是使用基於對象類型的if/elseif調度程序方法實現此操作,然後調用適當的實現。然而,最近我注意到,同樣的事情可以使用dynamic關鍵字來完成,像這樣:

private void ReconcileTips() 
{ 
    foreach (var step in _definition.Steps) 
    { 
     ReconcileTips((dynamic)step); 
    } 
} 

private void ReconcileTips(IBulkDispenseDefinition bulkDispense) 
{ 
    bulkDispense.TipType = ReconcileTip(bulkDispense.TipType); 
} 

private void ReconcileTips(ImportScreenDefinition importScreen) 
{ 
    foreach (var usage in importScreen.ReagentUsages) 
     usage.TipType = ReconcileTip(usage.TipType); 
} 

private void ReconcileTips(BuildScreenDefinition buildScreen) 
{ 
    foreach (var function in buildScreen.Functions) 
     function.TipType = ReconcileTip(function.TipType); 
} 

類似的模式可以用於其它操作並行的類結構,就像每個要素創建視圖模型_definition.Steps。我的想法是,編譯器基本上將其轉換爲我之前編寫的相同的邏輯,這爲我省下了很多努力。所以,幾個問題:

  1. 有沒有任何動態調度問題,我沒有考慮?我相信這相當於執行一系列if (x is TypeA) Do((TypeA)x) else...,但我可能是錯的。

  2. 這是否比長長的if/elseif方法更清晰易懂?

+0

除非我錯了,否則您的代碼似乎沒有任何問題。相反,您只是在檢查其他程序員對特定代碼段的感受。在這種情況下,[Code Review Stack Exchange](http://codereview.stackexchange.com)可能更適合這個問題? – 2013-05-01 21:09:24

+1

@Mark,這可能是一個更好的位置,我會與它正在遷移好;我想檢查的主要項目是(1)(是否存在與我沒有考慮過的動態使用相關的陷阱) – 2013-05-01 21:14:34

回答

9

是否有與動態調度任何陷阱,我還沒有考慮?我相信這相當於執行一系列if(x是TypeA)Do((TypeA)x)else,但我可能是錯的。

主要的疑難雜症是,如果一個類型實現你的訪問者模式不止一個界面 - 編譯器可能會挑選一個你想要的,但如果你使用if (x is TypeA)它可能不是你會作出同樣的選擇/ else if (x is TypeB)邏輯,因爲您會控制檢查發生的順序。

這是否比long if/elseif方法更清晰易懂?

我個人也這麼認爲。這提供了一個非常乾淨,相當體面的執行由運行時類型決定的調度,並且「正常工作」。很難打敗簡單,簡短,乾淨的代碼。只要確保(可能)處理的情況下,您會從傳入的錯誤類型中獲得運行時錯誤。

+0

很高興您提到了接口陷阱,因爲我實際上只是用'重疊'接口並記住了這個答案。 – 2013-05-22 20:45:53

1

是的,我正在考慮這種方法,但決定採用更傳統的方法。我從一個接口中派生每個訪問者,該接口對於我想要實現該操作的每種類型都具有訪問方法。

如果您有許多不同的操作要作爲訪問者實現,例如:保存和加載操作,您可能希望將來添加更多;使用動態方法,如果忘記爲需要處理的類型之一執行操作,則不會出現編譯錯誤。只有當程序在運行時崩潰並燒燬時纔會發現。

我想確保在編譯時已對所有可能的類型執行了任何操作。

+0

是的,在編譯時檢查好點;在添加新類型時偶爾會忘記實現其中一個訪問者。 – 2017-02-07 15:07:07