[英]Generic methods and method overloading
方法重載允許我們定義許多具有相同名稱但具有不同參數集(因此具有相同名稱但具有不同簽名)的方法。
這兩個方法重載了嗎?
class A
{
public static void MyMethod<T>(T myVal) { }
public static void MyMethod(int myVal) { }
}
編輯:
不應該聲明A<int>.MyMethod(myInt);
拋出錯誤,因為構造類型A<int>
有兩個具有相同名稱和相同簽名的方法?
這兩個方法是否重載?
是的。
不應該聲明
A<int>.MyMethod(myInt);
拋出錯誤,因為構造類型A<int>
有兩個具有相同簽名的方法?
這個問題沒有意義; A
不是您聲明的泛型類型。 也許你想問:
應該聲明
A.MyMethod(myInt);
導致編譯器報錯,因為有兩個不明確的候選方法?
不。正如其他人所說,在這種情況下,重載決議更喜歡非泛型版本。 有關更多詳細信息,請參見下文。
或者你可能想問:
類型 A 的聲明首先應該是非法的,因為在某種意義上它有兩個具有相同簽名的方法,
MyMethod
和MyMethod<int>
?
不,類型 A 是完全合法的。 通用 arity 是簽名的一部分。 因此,沒有兩個方法具有相同的簽名,因為第一個具有泛型元數為零,第二個具有泛型元數 1。
或者你可能想問:
class G<T>
{
public static void M(T t) {}
public static void M(int t) {}
}
可以構造泛型類型
G<T>
使其具有兩個具有相同簽名的方法。 聲明這樣的類型是否合法?
是的,聲明這樣的類型是合法的。 這通常是一個壞主意,但它是合法的。
然后你可能會反駁:
但是我的 Addison-Wesley 發布的 C# 2.0 規范副本在第 479 頁指出“使用相同名稱聲明的兩個函數成員......必須具有參數類型,使得封閉構造類型不能有兩個具有相同名稱的成員和簽名。 ”這是怎么回事?
最初設計 C# 2.0 時就是這樣計划的。 然而,隨后設計者意識到這種理想的模式將是非法的:
class C<T>
{
public C(T t) { ... } // Create a C<T> from a given T
public C(Stream s) { ... } // Deserialize a C<T> from disk
}
現在我們說對不起哥們,因為你可以說C<Stream>
,導致兩個構造函數統一,整個類都是非法的。 那將是不幸的。 顯然,任何人都不可能用 Stream 作為類型參數來構造這個東西!
不幸的是,規范在文本更新到最終版本之前就付諸出版了。 第 479 頁上的規則不是我們實施的。
繼續代表您提出更多問題:
那么如果你調用
G<int>.M(123)
或者,在原始示例中,如果你調用A.MyMethod(123)
什么?
當重載決議面臨由於泛型構造而具有相同簽名的兩種方法時,泛型構造的方法被認為比“自然”構造的方法“不那么具體”。 不太具體的方法輸給了更具體的方法。
那么,如果重載決議有效,為什么這是一個壞主意呢?
A.MyMethod
的情況還不錯; 通常很容易明確地確定要使用哪種方法。 但是G<int>.M(123)
情況要糟糕得多。 CLR 規則使這種情況成為“實現定義的行為”,因此任何舊的事情都可能發生。 從技術上講,CLR 可以拒絕驗證構造類型G<int>
。 或者它可能會崩潰。 事實上,它兩者都沒有; 它在糟糕的情況下盡其所能。
是否有任何此類類型構造導致真正實現定義行為的示例?
是的。 有關詳細信息,請參閱這些文章:
https://ericlippert.com/2006/04/05/odious-ambiguous-overloads-part-one/
https://ericlippert.com/2006/04/06/odious-ambiguous-overloads-part-two/
是的。 MyMethod(int myVal)
將在參數類型為int
調用,泛型重載將為所有其他參數參數調用,即使參數參數可隱式轉換為(或為派生類)硬編碼類型. 重載解析將是最合適的,並且泛型重載將在編譯時解析為完全匹配。
注意:正如 Steven Sudit 在他的回答中指出的那樣,您可以通過在方法調用中提供類型參數來顯式調用泛型重載並使用int
。
short s = 1;
int i = s;
MyMethod(s); // Generic
MyMethod(i); // int
MyMethod((int)s); // int
MyMethod(1); // int
MyMethod<int>(1); // Generic**
MyMethod(1.0); // Generic
// etc.
是的,他們是。 他們將允許這樣的代碼:
A.MyMethod("a string"); // calls the generic version
A.MyMethod(42); // calls the int version
是的,他們超載了。 如果通用方法可用,編譯器應該更喜歡顯式方法簽名而不是通用方法。 但是請注意,如果您可以避免這種過載,您可能應該這樣做。 已經有關於這種過載和意外行為的錯誤報告。
是的。 它們具有相同的名稱“MyMethod”但具有不同的簽名。 但是,C# 規范通過說編譯器將更喜歡非通用版本而不是通用版本來專門處理這個問題,當兩者都是選項時。
是的。 如果你調用A.MyMethod(1);
,它將始終運行第二種方法。 你必須調用A.MyMethod<int>(1);
強制它運行第一個。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.