程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> 開始聊聊C#泛型和委托(一)

開始聊聊C#泛型和委托(一)

編輯:關於C語言

在JAVA中,泛型只被JAVA編譯器支持,並不被JVM所支持,也就是說沒有定義新的字節碼來表示泛型類型,自然在JVM裡面也不會有新的指令來支持新的字節碼。類比到.NET來說,也就是被C#編譯器支持而不被CLR所支持。這樣就產生了很多有趣的問題。我們都知道我們的代碼都要經過編譯器的翻譯改動,JAVA中的泛型就是JAVA編譯器采用類型擦除的方式來實現泛型的。定義的泛型類型,都自動提供了一個相應的原始類型(raw type)原始類型的名字就是刪去類型參數後的泛型名,擦出掉類型變量,並替換為限定類型(無限定的變量用Object),可以看做是語法糖吧。比如:

view sourceprint?public class MyHashMap<TKey, TValue> { 

    private HashMap<TKey, TValue> m_map = new HashMap<TKey, TValue>(); 

      

    public TValue get(TKey key) { 

        return this.m_map.get(key); 

    } 

      

    public void put(TKey key, TValue value) { 

        this.m_map.put(key, value); 

    } 

  

    public static void main(String[] args) { 

        MyHashMap<String, Integer> map = new MyHashMap<String, Integer>(); 

        map.put("Hello", 5); 

        int i = map.get("Hello"); 

    }     

}

 

編譯成字節碼後,就成了下面這個樣子(這裡還用JAVA代碼來表示)

public class MyHashMap {    private HashMap m_map = new HashMap();        public Object get(Object key) {        return this.m_map.get(key);    }        public void put(Object key, Object value) {        this.m_map.put(key, value);    }    public static void main(String[] args) {        MyHashMap map = new MyHashMap();        map.put("Hello", 5);        int i = (Integer)map.get("Hello");    }    }好吧,看到Object,我承認我又想起裝箱了,可以看出Java中的泛型沒有解決裝箱問題。

由於JVM並不知道泛型類型,所以JAVA中就是以JAVA編譯器的語法糖的形式來表現的。當初我剛接觸JAVA的時候,的確會被下面幾種錯誤弄得很困惑。

view sourceprint?public class MyClass<SomeType> { 

    public static void myMethod(Object item) { 

        if (item instanceof SomeType) { // 報錯 

            ... 

        } 

        SomeType st = new SomeType(); // 報錯 

        SomeType[] myArray = new SomeType[10]; // 報錯 

    } 

}

 

 


在這裡我們可以想一下,到底怎麼樣才算真正的支持泛型呢?在.NET中,最終是由CLR根據元數據來執行IL代碼,因此,可以很容易理解:

1.IL中一定會有一個新指令來識別“類型參數”。

2.我們知道類型和方法的定義在元數據表中都會有相應的表示,因此為了支持泛型,元數據的格式也會有所改動。

3.修改JIT編譯器來執行新的IL指令。

也就是說,泛型類型定義能夠完整的編譯為MSIL類型。

 

泛型類型的運行大概的流程如下:

C#編譯器生成IL和元數據,表示泛型類定義,JIT編譯器則會把泛型類型定義與一系列的類型參數組合起來。

具體點來說,IL為初始化某個泛型類型的實例預留了占位符,JIT編譯器會在運行的時候,生成機器代碼的時候“補全定義”。JIT把相應的IL代碼編譯成X86指令,同時優化。優化什麼內容了呢?比如,在類型參數是引用類型的時候,就能使用相同的機器代碼來表示。為啥是引用類型而不是值類型呢?因為引用類型基本上都是指針,本質上來講結構都是一樣的。

這裡又要談一下類加載。JIT不是在某個類加載時就為其生成完整的X86指令,而是僅在類中的每個方法被第一次調用的時候才開始編譯的。(我現在覺得應該先講講類型,對象,線程棧和托管堆在運行時的相互關系比較好)。這樣,就會先在IL代碼上執行一個占位符替換步驟,替換成具體類型,隨後再像普通類一樣按需編譯。


好吧,你可以看出,在執行之前占位符被替換成具體類型了,因此泛型的匹配度是相當高的。應該說就是精確匹配。這個會影響什麼地方呢?在方法重載的時候就會有體現了。對於一個派生於MyBase的對象來說,WriteMEsaage<T>(T obj)要比WriteMEsaage(MyBase obj)在重載匹配上更優先。因為通過將T替換成MyDerived編譯器就可以完成一次“精確匹配”,而WriteMEsaage(MyBase obj)則還需要一次隱式轉換。於是泛型方法更有優勢,除非在調用時進行顯式類型轉換。下面用代碼說明:

view sourceprint?public class MyBase 

  

  

  

   

  

public class MyDerived : MyBase 

  

  

    #region IMessageWriter Members 

  

    void IMessageWriter.WriteMessage() 

  

    { 

  

        Console.WriteLine("Inside MyDerived.WriteMessage"); 

  

    } 

  

    #endregion 

  

  

   

  

class Program 

  

  

    static void WriteMessage(MyBase b)    { 

  

        Console.WriteLine("Inside WriteMessage(MyBase)"); 

  

    } 

  

    static void WriteMessage<T>(T obj) 

  

    { 

  

        Console.Write("Inside WriteMessage<T>(T):  "); 

  

        Console.WriteLine(obj.ToString()); 

  

    } 

  

    static void Main(string[] args) 

  

    { 

  

        MyDerived d = new MyDerived(); 

  

        Console.WriteLine("Calling Program.WriteMessage"); 

  

        WriteMessage(d); //讓編譯器推斷使用哪個匹配方法 

  

        Console.WriteLine(); 

  

        Console.WriteLine("Cast to base object"); 

  

        WriteMessage((MyBase)d); 

  

        Console.WriteLine(); 

  

    } 

  

}

 

因此當你想支持某一類及其所有派生類時,基於基類創建泛型並不是最好的選擇。同樣的,基於接口也是如此。

那麼我想針對,這時就需要通過運行時來判斷了,當然,這並不是最好的解決方案,雖然對調用者屏蔽了具體的實現,但同時會帶運行時檢查的開銷。

view sourceprint?Static void WriteMessage<T>(T obj){ 

  

         If(obje is MyBase){ 

  

                  WriteMessage(obj as MyBase);  //顯式類型轉換 

  

         }else { 

  

                  Conslole.Write(“Invoke WriteMessage<T>”) 

  

         } 

  

}

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved