2010-11-20 62 views
5

我偶然發現了一些代碼,我想知道這是否是最好的方法。我們有一種方法,它從一些web表單數據中獲取一個字符串,並根據傳入的字符串將數據轉換爲對象。目前,它使用反射來確定要採取的操作,但我想知道switch語句是否會更好。方法工廠 - 案例與反思

例子:

編輯:我添加了代表了第三種選擇,通過Lucerno

public class ObjectManipulator 
{ 
    private void DoX(object o) { } 
    private void DoY(object o) { } 
    private void DoZ(object o) { } 

    public void DoAction(string action, object o) 
    { 
     switch (action) 
     { 
      case "DoX": 
       DoX(o); 
       break; 
      case "DoY": 
       DoY(o); 
       break; 
      case "DoZ": 
       DoZ(o); 
       break; 
      default: 
       throw new Exception(string.Format(
        "Cannot locate action:{0}", action)); 
     } 
    } 

    public void DoActionViaReflection(string action, object o) 
    { 
     MethodInfo method = typeof(ObjectManipulator). 
      GetMethod(action, new Type[] { typeof(object) }); 
     if (method == null) 
     { 
      throw new Exception(string.Format(
       "Cannot locate action:{0}", action)); 
     } 
     else 
     { 
      method.Invoke(this, new object[] { o }); 
     } 
    } 
    private Dictionary<string, Action<object>> _methods; 
    public ObjectManipulator() 
    { 
     _methods = new Dictionary<string, Action<object>>() 
     { 
      {"DoX", o => DoX(o)}, 
      {"DoY", o => DoY(o)}, 
      {"DoZ", o => DoZ(o)} 
     }; 
    } 
    public void DoActionViaDelegates(string action, object o) 
    { 
     if (!_methods.ContainsKey(action)) 
     { 
      throw new Exception(string.Format(
       "Cannot locate action:{0}", action)); 
     } 
     else 
     { 
      _methods[action](o); 
     } 
    } 

} 

注意的第一個例子使用的開關,你可以看到可以得到非常詳細。第二短得多,但使用反射,我知道一些人像瘟疫一樣避免反射。

一種方法的性能會明顯好於另一種方法嗎?

如果有100個不同的動作而不是3個,性能是否會改變?

如果您正在閱讀的內容,您更喜歡在代碼中看到哪些內容?

+0

這幾乎是一個關於命令式和聲明式編程風格的問題。正如你從我的答案中可以看到的,Kirk似乎更喜歡命令式的方式,而我更喜歡後者。只是爲了明確這一點,不是「正確的」或「錯誤的」。 – Lucero 2010-11-20 19:21:14

回答

3

第一種情況幾乎總是會更快。然而,它的性能來自它可以在編譯期間被早期綁定的事實,但這也是它的最大缺點:例如,這種方法不能處理動態加載的程序集,並且它更容易出錯,因爲它是必要的而不是陳述性的。 (忘記例如新實施的行動可能會很快發生的事情。)

我通常的做法是用反射過程中發現的時間來實施這樣的模式,但在調用時使用委託。這使您獲得了反射方法的靈活性,其性能非常接近早期的方法。

  • 發現階段:使用反射來查找成員(使用屬性,接口,簽名和/或編碼約定)。在你的情況下,你總是有相同的簽名,所以使用的代表將是Action<object>。將這些成員添加到Dictionary<string, Action<object>>實例中,使用CreateDelegate()MethodInfo創建一個代理。

  • 調用階段:通過其獲得關鍵的委託並調用它,這是非常簡單(這裏假定該詞典被稱爲methods):methods[action](o)

2

性能不應該是你的關心,除非你個人資料這是一個瓶頸。 IMO更重要的是,你在反射版本中失去了靜態類型的安全和分析。在編譯時沒有辦法檢查是否正在調用那些動作方法DoXDOY等。這對你來說可能會也可能不是問題,但這是我最關心的問題。

而且,行動的數目是對於反射版本的性能完全無關緊要。 GetMethod在班上有很多成員時不​​會放慢速度。

+0

「MethodInfo」上的動態調用速度很慢,因此這將是性能最大的命中。 – Lucero 2010-11-20 19:10:31

+0

@Lucero,當然,它比較慢。問題在於它是否重要。根據我的經驗,它很少。 – 2010-11-20 19:13:01

2

如何使用代表?你可以使用類似的東西:

var Actions = new Dictionary<String, Action<object>>(); 
Actions["DoX"] = x => DoX(x); 
Actions["DoY"] = x => DoY(x); 
Actions["DoZ"] = x => DoZ(x); 

後來

public void DoAction(string action, object o) 
{ 
    Actions[action](o); 
} 

我想執行好,如果你有很多情況下,由於本字典有一個哈希查找。

你使用會帶來安全問題,如果用戶可以在另一個函數名給

+0

我懷疑這會編譯,因爲'DoX','DoY'和'DoZ'不是函數,但lambda期望返回值。 – Lucero 2010-11-20 19:23:34

+0

好了糾正它,「行動」是沒有任何返回值的委託的正確類型 – user287107 2010-11-21 00:47:06

2

您可以修復@ user287107的答案正是如此反射的類型:

var Actions = new Dictionary<String, Action<object>>(); 
Actions["DoX"] = DoX; 
Actions["DoY"] = DoY; 
Actions["DoZ"] = DoZ; 

這實際上是這樣做的發現階段的@ Lucero的答案明確,這可能是有用的,如果名稱不總是匹配。

如果可能的話,在一個枚舉中定義一組動作也會很不錯 - 因爲你不需要對這些字符串進行散列,所以速度會稍快一點。它也可以讓你編寫單元測試,以確保你沒有錯過任何潛在的價值觀。