2013-02-15 184 views
6

只是出於好奇,爲什麼編譯器將一個不受約束的泛型類型與它的typeof(object)不同?泛型和類型約束的規則

class Bar { } 

class Foo 
{ 
    void foo(object thing) 
    { 
     ((Bar)thing).ToString(); 
    } 
} 

class Foo<T> 
{ 
    void foo(T thing) 
    { 
     ((Bar)thing).ToString(); 
    } 
} 

在上面,將「T thing」強制轉換爲Bar會導致編譯器錯誤。然而,向Bar提供「object thing」是編譯器允許我做的事情,當然,我自己承擔風險。

我不明白的是爲什麼。在.net對象中,所有內容都是全部捕獲的,而運行時類型可能是盒裝值或任何類型的對象。所以我看不出有什麼合乎邏輯的理由讓編譯器區分這兩種情況。我能做的最好的事情就像「程序員希望編譯器使用泛型類型進行類型檢查,但不使用對象」。 :)這一切都有它嗎?

順便說一句,我知道我還可以得到我的演員在美孚的情況下完成的,通過寫

((Bar)(object)thing).ToString(); 

我只是想了解爲什麼編譯器做到這一點...

+3

編譯時間是否合法將'int'轉換爲'Bar'?當你用'int'填充那個類型參數時,它應該*然後*開始有編譯器錯誤嗎?如果裝配不是你的,那你怎麼看不出問題呢? T不是客體。這是非常具體的東西。 – 2013-02-15 14:12:09

+1

你是否也知道你可以說'Foo 其中T:Bar'來保證'T'總是可以投射到'Bar'? – Rawling 2013-02-15 14:12:12

+1

我確信Eric Lippert在這個地方有一篇博客文章,但我找不到它...... – 2013-02-15 14:13:57

回答

4

的這裏的意義是object。如果第一個例子是其他任何非object它將表現相同。基本上,你在說什麼,此刻在這裏:

(Bar)thing 

是:「轉換TBar」;這在一般情況下並不合法。通過添加object你把它:

(Bar)(object)thing 

這是「轉換Tobject ...」 - 這始終是合法的,因爲object是所有託管類型的根;並注意這可能會啓用一個框 - 「...然後將object作爲Bar」 - 再次;它在編譯時總是合法的,並且在運行時涉及類型檢查(「unbox-any」)。

例如:假設TDateTime ...

DateTime thing = ... 
Bar bar = (Bar)(object)thing; 

是完全有效;確定它在運行時會失敗,但是:這是你需要牢記的場景。

+0

雖然正確,但我認爲操作系統要求設計決策背後的選擇是在* compile *時間後面的情況下顯示錯誤,而在*前面顯示前者的時間(假設事物不能轉換爲「Bar」。 – 2013-02-15 14:17:44

+0

我認爲這是一個很好的解釋。將對象轉換爲某個東西通常是一種沮喪,更具體的東西,但一般情況下可以是,也可以是*構造的*類型不會編譯任何T - 這是給編譯時錯誤的一個很好的理由! – 2013-02-22 16:24:45

4

它歸結爲創建泛型的語義和目的。如果你有一個通用類型T,編譯器不會讓你直接將它直接轉換爲任何其他對象。這是有道理的,因爲T的目的是強制程序員指定T實際是什麼類型。它不會是「對象」,它將成爲特定類型的對象。在編譯時,編譯器無法知道T中將會發生什麼,因此無法投射它。

從對象中投射的作品,因爲它是一個匿名對象 - 與KNOWN對象類型相反,它在使用中被定義。

這可以用「where」子句進行擴展。例如。,你可以指定T必須是IBar類型;

interface IBar { } 

class Bar : IBar { } 

class Foo<T> 
    where T : IBar 
{ 
    void foo(T thing) 
    { 
     ((IBar)thing).ToString(); 
    } 
} 

繼承也適用於where子句;

class Bar { } 

class Foo<T> 
    where T : Bar 
{ 
    void foo(T thing) 
    { 
     // now you don't need to cast at all as the compiler knows 
     // exactly what type T is (at a parent level at least) 
     thing.ToString(); 
    } 
} 
+0

我很高興將此標記爲答案,但我發現你的推理比我原來的建議多一點;它確實沸騰直到「用戶」(我的泛型類的用戶,或者確切地說是一個已經定義了T的構造類)的期望。要說「T不僅僅是一個對象,它是特定的」,恕我直言,任何物體都可以,通常是非常sp的東西ecific。有不同的方法來查看同一個對象。我可能想要支持任何類型,但是如果類型是我知道的類型,則以特定方式運行,例如 - 通用類或不是。 – 2013-02-22 16:22:23

+0

@TheDag儘管存在處理特定類型的有效情況,例如,裝箱或非裝箱類型,但基於類型T改變泛型的行爲通常不是一個好主意。在你的例子中,你試圖直接從T中獲得,默認情況下,它幾乎肯定會成爲另一種特定類型「Bar」的特定類型。當沒有關係或通用接口時,這將嘗試嘗試執行'(Foo)Bar'。我仍然認爲編譯器警告是有效的 – DiskJunky 2013-02-22 16:29:11