2009-08-18 52 views
18

考慮下面的類,調用構造函數重載時都超載具有相同簽名

class Foo 
{ 
    public Foo(int count) 
    { 
     /* .. */ 
    } 

    public Foo(int count) 
    { 
     /* .. */ 
    } 
} 

上面的代碼是無效的,將無法編譯。現在考慮下面的代碼,

class Foo<T> 
{ 
    public Foo(int count) 
    { 
     /* .. */ 
    } 

    public Foo(T t) 
    { 
     /* .. */ 
    } 
} 

static void Main(string[] args) 
{ 
    Foo<int> foo = new Foo<int>(1); 
} 

上面的代碼是有效的,編譯好。它調用Foo(int count)

我的問題是,如果第一個無效,第二個如何有效?我知道類Foo <T>是有效的,因爲T和int是不同的類型。但是當它使用像Foo <int> foo = new Foo <int>(1),T得到整數類型和兩個構造函數將具有相同的簽名權?爲什麼編譯器不顯示錯誤而不是選擇一個重載執行?

回答

19

時正在設計C#2.0,並在CLR通用類型系統您的問題已引起激烈辯論。實際上,事實上,A-W發佈的「綁定」C#2.0規範實際上有一個錯誤的規則!有四種可能性:

1)是非法的聲明泛型類,可能被一些建築之下曖昧。 (這是綁定規範錯誤地說是規則。)所以你的Foo<T>聲明是非法的。

2)是非法的構建體,其中將導致不確定性的方式的通用類。宣稱Foo<T>將是合法的,構建Foo<double>將是合法的,但構建Foo<int>將是非法的。

3)使這一切合法,並使用重載的技巧,制定出通用或者非泛型的版本是否是更好的。 (這是C#實際上沒有。)

4)做別的東西,我都沒有想到的。

規則1是一個壞主意,因爲它使一些非常普通和無害的情況變得不可能。考慮例如:

class C<T> 
{ 
    public C(T t) { ... } // construct a C that wraps a T 
    public C(Stream state) { ... } // construct a C based on some serialized state from disk 
} 

你想這是非法的只是因爲C<Stream>是不明確的?呸。規則#1是一個壞主意,所以我們放棄了它。

不幸的是,它並不那麼簡單。 IIRC CLI規則指出,允許實現拒絕作爲實際上會導致簽名歧義的非法構造。也就是說,CLI規則類似於規則2,而C#實際上實現了規則3。這意味着理論上可能有合法的C#程序轉化爲非法代碼,這是非常不幸的。

有關這些種種模糊之處,如何使我們的生活猥瑣的一些想法,這裏有幾篇文章我關於這個問題寫道:

http://blogs.msdn.com/ericlippert/archive/2006/04/05/569085.aspx

http://blogs.msdn.com/ericlippert/archive/2006/04/06/odious-ambiguous-overloads-part-two.aspx

+0

猜測第二個Foo <>在2必須是int – Dykam 2009-08-18 16:42:36

+0

「合法的C#程序可以翻譯成非法代碼」這怎麼可能?我認爲翻譯結果是一個'newobj'指令和一個方法標記,其中'Foo .ctor(int)'和'Foo .ctor(T)'具有不同的方法標記。所以在IL中沒有含糊之處。我錯過了什麼? – 2015-02-23 00:48:26

+1

@BenVoigt:首先,如果我說「不可驗證的代碼」或「具有實現定義的行爲的代碼」,那麼代替「非法代碼」就會更準確。產生簽名碰撞的通用結構的後果是微妙的。假設我們有兩個具有相同簽名的方法,並希望在元數據中指出其中一個方法是實現特定的接口方法。沒有可以在方法實現表中使用相同簽名的兩種方法消除歧義的元數據結構。還有其他類似的奇怪情況。 – 2015-02-23 19:18:45

23

沒有歧義,因爲編譯器會選擇Foo(...)相匹配的最具體的過載。由於具有泛型類型參數的方法被認爲比相應的非泛型方法更具體,因此當T == int時,Foo(T)因此不如Foo(int)特定。因此,您正在調用Foo(int)過載。

你的第一種情況(有兩個Foo(int)定義)是錯誤的,因爲編譯器只允許一個具有完全相同簽名的方法的定義,而你有兩個。

+0

我查了語言規範,但並沒有說通用參數不如具體類型具體。然而,非泛型方法被認爲比通用方法「更好」。 – Thorarin 2009-08-18 11:27:28

+0

你說得對,那句話不是很好。我的意思是說「具有泛型類型參數的方法...」。 – 2009-08-18 11:48:43

+0

順便說一句,對於好奇,我認爲Thorarin所討論的部分是§14.4.2.2,它定義了「更好的函數成員」。 – 2009-08-18 11:49:38

9

Eric Lippert blogged關於這個最近。

+1

而且,顯然他也在這裏回答了這個問題。 – 2009-08-18 20:24:02

0

的事實是,他們不都具有相同的簽名 - 一個使用泛型,而這另一個則不是。

有了這些方法的地方,你也可以使用非int對象調用它:

Foo<string> foo = new Foo<string>("Hello World"); 
相關問題