2011-11-08 65 views
11

我無法讓C#編譯器調用我創建的擴展方法,因爲它傾向於帶有params參數的實例方法。如何用params強制使用擴展方法而不是實例方法?

例如,說我有下面的類和方法:

public class C 
{ 
    public void Trace(string format, params object[] args) 
    { 
     Console.WriteLine("Called instance method."); 
    } 
} 

而且與延伸:

public static class CExtensions 
{ 
    public void Trace(this C @this, string category, string message, params Tuple<string, decimal>[] indicators) 
    { 
     Console.WriteLine("Called extension method."); 
    } 
} 

在我的示例程序:

pubic void Main() 
{ 
    var c = new C(); 

    c.Trace("Message"); 
    c.Trace("Message: {0}", "foo"); 
    c.Trace("Category", "Message", new KeyValuePair<string, decimal>("key", 123)); 
} 

所有這些都需要打印Called instance method.

顯然,我無權訪問類C,或者我不打算創建擴展方法,而且我的擴展很重要,因爲它可以讓我的用戶繼續使用他們已知的具有某種附加值的類。

從我的理解,編譯器將贊成實例方法通過擴展方法,但這是唯一的規則?這意味着任何類似於Method(string format, params object[] args)的方法都不能使用類型爲string的第一個參數的擴展方法。

任何解釋此行爲或解決方法的原因(即而不是「只需致電CExtensions.Trace(c, "Category", ...」)將不勝感激。

+0

我以前遇到過這個。似乎唯一的解決辦法是重命名該方法。我有興趣看看是否有修復。 – Polynomial

回答

5

您不能使用擴展來「接管」現有的類方法。

如果該呼叫在沒有擴展名的情況下工作,那麼添加擴展名時行爲不應該改變。原因是現有的代碼不應該通過稍後引入擴展來解決。

您必須爲其使用不同的名稱,或者使用與類方法不同的參數類型。

+0

儘管很多人提出了很好的解決方法建議(即使沒有人爲我工作),但我認爲這是最好的答案,因爲它完美地總結了我的問題來自哪裏。 – madd0

4

您不能直接。目標實例上的方法始終優於擴展方法。 只有方式做到這一點(同時保持名稱相同等)是使用CExtensions.Trace方法(在問題中)。

在一些情況下,一個特技這裏是使用一些基類的C或接口C器具,但不具有一個Trace方法,並重新鍵入變量,並在添加過載CExtensions,即

IFoo foo = c; 
foo.Trace(...); 
+0

我喜歡將擴展方法添加到基本類型的技巧。當然,這將是太容易了,在我的情況下,我不得不延長'object' ... – madd0

0

我想你可以改變第一個參數類型或函數名(TraceFormat?)

這PARAMS版本看起來很貪心,所以如果你保存的第一個參數爲海峽它總是會接到所有的電話,並忽略我相信的擴展方法。

+0

我真的不想改變方法名稱,因爲這是開發人員習慣的東西。更改名稱意味着該方法不會按我們的意願使用。將第一個參數的類型更改爲「string」以外的類型會很困難,因爲我真的希望將一個類別名稱作爲「字符串」。 – madd0

+0

實際上第一個參數是類似於它可能是一個很好的候選人成爲一個枚舉例如,它會開始工作。 –

+0

瓦倫丁,很好的嘗試,但對於一些球隊來說,「類別」可能是,例如,客戶的名字。因爲我真的希望我們的客戶羣保持增長,所以我寧願避免一個'enum',它必須不斷更新; – madd0

2

Tuple<string, decimal>KeyValuePair<string, decimal>不一樣。因此傳入的KeyValuePair<string, decimal>僅作爲對象使用,因此使用params object[] args的成員方法。

實際上KeyValuePair結構

+0

'Tuple'或'KeyValuePair',它並沒有真正改變太多,這兩種類型(_any_類型)都會被'object'數組「吸收」。 – madd0

+0

這是真的,但如果你在調用中使用'Tuple ',它將不會使用擴展方法。 – Guffa

+0

是的,我剛剛測試過,它沒有使用它。 – Aliostad

2

你可以用命名參數做到這一點:

c.Trace(
    "Category", 
    "Message", 
    indicators: new Tuple<string, decimal>("key", 123)); 

,但你失去了params功能,你需要明確地傳遞一個數組的指標參數,如下所示:

c.Trace(
    "Category", 
    "Message", 
    indicators: new Tuple<string, decimal>[] 
    { 
     new Tuple<string, decimal>("key", 123), 
     new Tuple<string, decimal>("key2", 123) 
    }); 
+0

它會工作,但它很容易忘記這個命名的參數,我們將默默地調用默認版本,但沒有辦法注意到這一點。 –

+0

@Valentin Kuzub,但其他選項也容易出錯。您可能會忘記您需要使用不同的名稱或不同的簽名來調用該方法。 –

+0

João的論點正是我不想改變方法名稱或簽名的原因,但是Valentin的權利:很可能會忘記使用命名參數並且在沒有注意的情況下調用默認版本(或者只有當它太遲)。 – madd0