2010-02-09 53 views
5

好吧,我想做到以下幾點:方法重載:漏斗調用派生類的說法超載

 protected bool ValidAdvert(Base item) 
     { 
      throw ThisIsAnAbstractClassException(); 
     } 

     protected bool ValidAdvert(Derived1 item) 
     { 
      return ADerived1SpecificPredicate; 
     } 

     protected bool ValidAdvert(Derived2 item) 
     { 
      return ADerived2SpecificPredicate; 
     }

而且有方法的派生類版本中的,當一個基類被傳遞到方法。基類是抽象的,所以這在理論上應該是可能的?

在有人說過關於重載方法本身的方法之前,方法中的邏輯依賴於大量不同的條件,這些條件中沒有一個是相關的,並且它們都不直接與Base/Derived類相關(如登錄狀態等)

回答

6

如果您發現雙分派過於侵入,您可以通過反射調用該方法並動態解決適當的過載。

protected bool ValidAdvert(Base item) 
{ 
    if (item.GetType() == typeof(Base)) 
     throw new ThisIsAnAbstractClassException(); 

    Type type = typeof(CurrentClass); 

    MethodInfo method = type.GetMethod("ValidAdvert", 
             BindingFlags.Instance | BindingFlags.NonPublic, 
             null, 
             new Type[] { item.GetType() }, 
             null); 
    return (bool)method.Invoke(this, new object[] { item }); 
} 

protected bool ValidAdvert(Derived1 item) 
{ 
    return ADerived1SpecificPredicate; 
} 

protected bool ValidAdvert(Derived2 item) 
{ 
    return ADerived2SpecificPredicate; 
} 

這種模式被稱爲MultipleDispatch,同時通過反射調用一個方法是不是直接調用方法(它不會是,如果該方法被稱爲小於每秒一百次數的開銷),它具有更慢的不需要像雙調度模式那樣修改類層次結構的好處。

在C#4.0的動態關鍵字出來這將是更容易:

protected bool ValidAdvert(Base item) 
{ 
    if (item.GetType() == typeof(Base)) 
     throw new ThisIsAnAbstractClassException(); 

    dynamic dynamicThis = this; 

    return (bool)dynamicThis.ValidAdvert(item as dynamic); 
} 
+0

也很聰明,並避免與各種派生類混淆(我希望只有2!)。 –

+0

相當的筆記:你需要在返回時投射到bool,並且method.Invoke需要一個[]對象,所以如果你嘗試並傳遞1,那麼這樣就會發癢:)但是謝謝! –

+0

@Ed - 我認爲保留派生類的謂詞實際上是首選方法。將派生類特定邏輯存儲在基類中會在兩者之間建立反向耦合。我不認爲這是一件好事。基類不需要知道其派生類的任何內容。 – tvanfosson

5

用一個完美的地方double dispatch

class Base 
{ 
    public abstract bool IsValid(IAdvertValidator validator); 
} 

class Derived1 : Base 
{ 
    public bool IsValid(IAdvertValidator validator) 
    { 
     return validator.ValidAdvert(this); 
    } 
} 

class Derived2 : Base 
{ 
    public bool IsValid(IAdvertValidator validator) 
    { 
     return validator.ValidAdvert(this); 
    } 
} 

interface IAdvertValidator 
{ 
    bool ValidAdvert(Derived1 d1); 
    bool ValidAdvert(Derived2 d2); 
} 
+0

聰明,我喜歡它。 –

+0

該模式的合理使用。另一種方法是從驗證程序中公開驗證方法,並在IsValid的實現中使用它們,從而保持類中控件的實際驗證流程。 – tvanfosson

0

我不知道你到底如何永遠不會有基類實際上不是一個實例的實例派生類。由於基類是抽象的,因此它不能被實例化。我想它會匹配任何派生類,沒有特定的簽名,但這似乎不是你要去的。

+0

是的,它始終是派生類之一,但是,它在技術上也是基類,這意味着當我這樣做時會調用基類方法(列表)例如,i.Where(ValidateAdvert)。 –

+0

啊 - 我正在閱讀你的代碼示例,而不是你的問題。您實際上不希望它引發異常,而是希望它調用其他方法之一。在那種情況下,我會選擇@ Anton的建議或類似的東西。請注意,您可以在構造函數中提供驗證器,以便您不需要將它作爲參數傳遞。我還使用了一個靜態驗證類來驗證簡單的屬性,不需要保存對它的引用。但是,你需要小心這些,因爲如果你不小心的話,它們可以使測試變得更加困難。 – tvanfosson

+0

驗證器是一個頁面和狀態特定的類,因此不能很好地作爲靜態工作,但我認爲它會在反射方法中很好地運行,這是一種遺憾,這是默認情況下不支持的雖然! –