2012-07-11 142 views
10

我曾經想過,C#中的泛型被實現,以便在運行時或編譯時生成一個新的類/方法/你什麼時候使用過新的泛型類型,類似於C++模板(我從來沒有真正看過,我很可能是錯的,我很樂意接受更正)。C#泛型如何實現?

但在我的編碼,我想出了一個確切的反例:

static class Program { 
    static void Main() 
    { 
     Test testVar = new Test(); 

     GenericTest<Test> genericTest = new GenericTest<Test>(); 
     int gen = genericTest.Get(testVar); 

     RegularTest regTest = new RegularTest(); 
     int reg = regTest.Get(testVar); 

     if (gen == ((object)testVar).GetHashCode()) 
     { 
      Console.WriteLine("Got Object's hashcode from GenericTest!"); 
     } 
     if (reg == testVar.GetHashCode()) 
     { 
      Console.WriteLine("Got Test's hashcode from RegularTest!"); 
     } 
    } 

    class Test 
    { 
     public new int GetHashCode() 
     { 
      return 0; 
     } 
    } 

    class GenericTest<T> 
    { 
     public int Get(T obj) 
     { 
      return obj.GetHashCode(); 
     } 
    } 

    class RegularTest 
    { 
     public int Get(Test obj) 
     { 
      return obj.GetHashCode(); 
     } 
    } 
} 

的控制檯線印刷的兩個。

我知道發生這種情況的真正原因是對Object.GetHashCode()的虛擬調用不能解析爲Test.GetHashCode(),因爲Test中的方法標記爲新的而不是覆蓋。因此,我知道如果我在Test.GetHashCode()上使用「override」而不是「new」,那麼0的返回將多態地覆蓋對象中的方法GetHashCode,這不是真的,但根據我(以前)的理解的C#泛型不重要,因爲T的每個實例都將被替換爲Test,因此方法調用將靜態地(或在通用的解析時間)解析爲「new」方法。

所以我的問題是這樣的:如何泛型在C#中實現?我不知道CIL字節碼,但我知道Java字節碼,所以我理解面向對象的CLI語言如何在低級別上工作。隨意在該級別解釋。另外,我認爲C#泛型是以這種方式實現的,因爲與Java的類型擦除系統相比,每個人都總是用C#「True Generics」調用通用系統。

+0

任何原因轉換爲對象在這裏'gen ==((object)testVar).GetHashCode()'? – AlwaysAProgrammer 2012-07-11 16:11:21

+0

雖然它不直接回答你的問題,但http://blogs.msdn.com/b/ericlippert/archive/2012/07/10/when-is-a-cast-not-a-cast.aspx有一些好的有關泛型如何投射以及它們在C#中如何相互關聯的信息。 – devstruck 2012-07-11 16:11:40

+0

@Yogendra這樣做訪問Object.GetHashCode()方法而不是「new」方法Test.GetHashCode()。這就是爲什麼它返回一個不同的值(因爲它完全運行一個不同的方法)。 – Carrotman42 2012-07-11 16:19:07

回答

7

GenericTest<T>.Get(T)中,C#編譯器有已經挑選object.GetHashCode應該被調用(虛擬)。在運行時(它將在方法表中有自己的插槽,而不是覆蓋插槽object.GetHashCode),這種方法無法解析爲「新」GetHashCode方法。

從埃裏克利珀的What's the difference, part one: Generics are not templates,這個問題解釋(用來設置略有不同,但教訓很好的轉化爲您的方案):

這說明,在C#泛型是不喜歡的模板在C++中。 你可以將模板看作是一個花式搜索替換 機制。[...]這不是泛型類型的工作方式;通用類型有, 好,通用。我們做超載分辨率一次並烘烤 結果。 [...]我們爲通用類型生成的IL已經有 它將要調用的方法。抖動並不是說 「好吧,我碰巧知道,如果我們要求C#編譯器現在執行 這個附加信息,那麼它會選擇一個不同的過載。讓我重寫生成的代碼以忽略C#編譯器最初生成的代碼 ...「抖動知道 沒有任何關於C#的規則。

和解決方法爲您所需的語義:

現在,如果你想將基於運行時類型的 參數在運行時重新執行重載決議,我們能做到這一點的您;這就是C#4.0中新的「動態」功能 。只需將「object」替換爲「dynamic」,並且當您調用涉及該對象的調用時,我們將在運行時運行過載 解析算法,並動態地吐出代碼,調用編譯器會選擇的方法,如果知道該方法編譯時所有的 運行時類型。

+3

阿里埃裏克,如果沒有你,我們會怎麼做。 – Servy 2012-07-11 16:26:02

+0

是的,我說在第一段「我知道這種情況發生的真正原因是......」我的問題是「C#泛型如何實現?」我會閱讀你發送的鏈接,也許這將回答我的問題。 – Carrotman42 2012-07-11 16:30:05

+0

@你的編輯:這不是我想要做的:我來自Java的背景,那裏沒有整個「新方法」的考驗。我認爲這很容易出現用戶錯誤,我不打算有意使用它。我碰到它的原因是因爲我正在寫一個抽象類,我想強制子類來實現GetHashCode,所以我寫了「public abstract int GetHashcode();」,沒有意識到爲了讓這個任務一般調用GetHashCode,我不得不實際說「public override abstract int GetHashcode();」,這對我的口味來說非常冗長。 – Carrotman42 2012-07-11 16:36:23