2013-04-22 65 views
6

所以我有一個對象層次結構在asp.net mvc中生成ui控件並嘗試實現流暢的api。我做了一些虛擬課,專注於當前的問題。
因此,這裏是「錯誤」的基本代碼:如何在.NET中「重寫」擴展方法?

public abstract class HtmlElement { /* ... */ } 

public abstract class UIElement : HtmlElement { /* ... */ } 

public abstract class ButtonBase : UIElement { /* ... */ } 

public class LinkButton : ButtonBase { /* ... */ } 

public class ActionButton : ButtonBase { /* ... */ } 


public static class HtmlElementExtensions 
{ 
    public static T Id<T>(this T item, string id) where T : HtmlElement 
    { 
    /* set the id */ 
    return item; 
    } 
} 

public static class ButtonBaseExtensions 
{ 
    public static T Id<T>(this T item, string id) where T : ButtonBase 
    { 
    /* set the id and do some button specific stuff*/ 
    return item; 
    } 
} 

當我嘗試撥打標識上一個LinkBut​​ton編譯器說有曖昧電話:

LinkButton lb = new LinkButton().Id("asd"); 

我真的以爲編譯器選擇在這種情況下最接近的匹配,所以如果我有一個從HtmlElement繼承的Script類而不是HtmlExtensions Id方法調用,並且對於LinkBut​​ton(因爲有限制),ButtonBase方法將被調用。 我有一個解決方案,但我不確定是否有更好的解決方案。
我刪除ButtonBaseExtensions的標識方法和改進的HtmlElementExtensions標識方法,方式如下:

public static T Id<T>(this T item, string id) where T : HtmlElement 
{ 
    if (item is ButtonBase) 
    { 
    /* do some button specific stuff*/ 
    } 
    /* set the id */ 
    return item; 
} 

這樣,從在ButtonBase每類傳人工作。 我不喜歡我的解決方案,因爲它將HtmlElement邏輯與ButtonBase邏輯混合在一起。 任何想法/建議更好的解決方案? 我以爲我把它們放在不同的命名空間,但只是一秒鐘。我應該使用兩個名稱空間,所以不要解決問題。

你認爲在msdn論壇上值得一提的是編譯器應該關注通用擴展方法的限制嗎?

在此期間我做一些更多的研究,並在MSDN論壇啓動一個線程:link
我嘗試了一些非通用擴展methodds:

public class BaseClass { /*...*/ } 
    public class InheritedClass : BaseClass { /*...*/ } 

    public static class BaseClassExtensions 
    { 
    public static void SomeMethod(this BaseClass item, string someParameter) 
    { 
     Console.WriteLine(string.Format("BaseClassExtensions.SomeMethod called wtih parameter: {0}", someParameter)); 
    } 
    } 

    public static class InheritedClassExtensions 
    { 
    public static void SomeMethod(this InheritedClass item, string someParameter) 
    { 
     Console.WriteLine(string.Format("InheritedClassExtensions.SomeMethod called wtih parameter: {0}", someParameter)); 
    } 
    } 

如果我舉例說明這些:

BaseClass bc = new BaseClass(); 
InheritedClass ic = new InheritedClass(); 
BaseClass ic_as_bc = new InheritedClass(); 

bc.SomeMethod("bc"); 
ic.SomeMethod("ic"); 
ic_as_bc.SomeMethod("ic_as_bc"); 

產生此輸出:

BaseClassExtensions.SomeMethod called wtih parameter: bc 
InheritedClassExtensions.SomeMethod called wtih parameter: ic 
BaseClassExtensions.SomeMethod called wtih parameter: ic_as_bc 

You can vote it for now

感謝,
彼得

+1

擴展方法是** **不爲虛方法的替代品,也不能發虛。考慮到你自己宣佈了這些課程,爲什麼你需要它,這是相當不清楚的。只需將一個虛擬方法添加到其中一個基類中,並在必要時在派生類中重寫它。 – 2013-04-22 11:11:58

+0

由於流利的API,我必須使用擴展方法。泛型繼承僅適用於2級繼承。 我寫了一個非genric擴展方法的示例,其中編譯器使用參數限制來確定調用的正確方法。我只希望編譯器也評估通用參數限制。我不能寫「公共靜態ButtonBase ID(這ButtonBase項目,字符串ID)」而不是「公共靜態T Id (這T項目,字符串ID)其中T:ButtonBase」,因爲返回類型將ButtonBase和它破流利api的方法鏈。 – 2013-04-22 13:58:02

回答

2

你可以把有關擴展方法的MSDN文檔上一看:Extension Methods (C# Programming Guide)。有趣的部分是在編譯時綁定擴展方法:

...它首先會在該類型的實例方法匹配。如果找不到匹配項,它將搜索爲該類型定義的任何擴展方法,並綁定到第一個擴展方法,它找到

所以這就是爲什麼你看到這種行爲。我真的可以買到它,試想一下,有人可以用一種方法覆蓋應用程序的工作方式public static T Id<T>(this T item, string id) where T : object。如果你不會看到任何編譯器錯誤,你會認爲一切都是正確的,也許一切都會工作,除了一些很少的情況。它有多混淆?

還有一個關於你的方法的壞處。如果我會消費你的API,並會看到按鈕我有兩種方法:一個在HtmlElementExtensions和一個在ButtonBaseExtensions什麼會阻止我做這個HtmlElementExtensions.Id(button, "id")而不是ButtonExtensions.Id(button, "id")

在你的情況下,我寧願相結合的辦法:

public static T Id<T>(this T item, string id) where T : HtmlElement 
{ 
    if (item is ButtonBase) 
    { 
     return (T)Id((ButtonBase)item); 
    } 
    else if (item is HtmlElement) 
    { 
     return (T)Id((HtmlElement)item); 
    } 

    throw new NotSupportedException("Type " + item.GetType() + " is not supported by Id extension method"); 
} 

private static ButtonBase Id(ButtonBase item, string id) 
{ 
    return item; 
} 

private static HtmlElement Id(HtmlElement item, string id) 
{ 
    return item; 
} 
+0

用我的例子,用戶不能覆蓋我的方法,因爲在繼承樹中,ButtonBase比對象更接近LinkBut​​ton。而且你認爲是非常惡意的用戶。我認爲添加新的Exception()會更簡單;給代碼做錯誤。 :D 你的答案看起來像我的解決方案,只是你的一點更優雅:) – 2013-04-23 08:34:31

+0

@佩特是的,對不起。我正在考慮不同的方向。如果你有'ButtonBase',有人會寫'LinkBut​​ton'的擴展方法。是的,通過「組合方式」,我的意思是同時提供您的解決方案;) – outcoldman 2013-04-23 17:01:32