泛型編程
泛型編程
泛型編程(Generic Programming)最初提出時的動機很簡單直接:發明一種語言機制,能夠幫助實現一個通用的標準容器庫。所謂通用的標準容器庫,就是要能夠做到,比如用一個List類存放所有可能類型的對象這樣的事;泛型編程讓你編寫完全一般化並可重複使用的演演算法,其效率與針對某特定數據類型而設計的演演算法相同。泛型即是指具有在多種數據類型上皆可操作的含義,與模板有些相似。STL巨大,而且可以擴充,它包含很多計算機基本演演算法和數據結構,而且將演演算法與數據結構完全分離,其中演演算法是泛型的,不與任何特定數據結構或對象類型系在一起。
泛型編程的代表作品STL是一種高效、泛型、可交互操作的軟體組件。STL以迭代器 (Iterators)和容器(Containers)為基礎,是一種泛型演演算法(Generic Algorithms)庫,容器的存在使這些演演算法有東西可以操作。STL包含各種泛型演演算法(algorithms)、泛型迭代器(iterators)、泛型容器(containers)以及函數對象(function objects)。STL並非只是一些有用組件的集合,它是描述軟體組件抽象需求條件的一個正規而有條理的架構。
泛型的第一個好處是編譯時的嚴格類型檢查。這是集合框架最重要的特點。此外,泛型消除了絕大多數的類型轉換。如果沒有泛型,當你使用集合框架時,你不得不進行類型轉換。
關於泛型的理解可以總結下面的一句話,它是把數據類型作為一種參數傳遞進來。
泛型編程(Generic Programming)最初提出時的動機很簡單直接:發明一種語言機制,能夠幫助實現一個通用的標準容器庫。所謂通用的標準容器庫,就是要能夠做到,比如用一個List類存放所有可能類型的對象,這樣的事情;熟悉一些其它面向對象的語言的人應該知道,如Java裡面這是通過在List裡面存放Object引用來實現的。Java的單根繼承在這裡起到了關鍵的作用。然而單根繼承對C++這樣的處在語言鏈底層的語言卻是不能承受之重。此外使用單根繼承來實現通用容器也會帶來效率和類型安全方面的問題,兩者都與C++的理念不相吻合。
泛型編程最初誕生於C++中,由Alexander Stepanov和David Musser創立。目的是為了實現C++的STL(標準模板庫)。其語言支持機制就是模板(Templates)。模板的精神其實很簡單:參數化類型。換句話說,把一個原本特定於某個類型的演演算法或類當中的類型信息抽掉,抽出來做成模板參數T。比如qsort泛化之後就變成了:
template
void sort(RandomAccessIterator first, RandomAccessIterator last,
Compare comp);
其中first,last這一對迭代器代表一個前閉后開區間,迭代器和前閉后開區間都是STL的核心概念。迭代器建模的是內建指針的介面(解引用、遞增、遞減等)、前閉后開區間是一個簡單的數學概念,表示從first(含first)到last(不含last)的區間內的所有元素。此外,comp是一個仿函數(functor)。仿函數也是STL的核心概念,仿函數是建模的內建函數的介面,一個仿函數可以是一個內建的函數,也可以是一個重載了operator()的類對象,只要是支持函數調用的語法形式就可成為一個仿函數。
通過操作符重載,C++允許了自定義類型具有跟內建類型同樣的使用介面;又通過模板這樣的參數化類型機制,C++允許了一個演演算法或類定義,能夠利用這樣的介面一致性來對自身進行泛化。例如,一個原本操作內建指針的演演算法,被泛化為操縱一切迭代器的演演算法。一個原本使用內建函數指針的演演算法,被泛化為能夠接受一切重載了函數調用操作符(operator())的類對象的演演算法。
C#泛型代碼在被編譯為IL代碼和無數據時,採用特殊的佔位符來表示泛型類型,並用專有的IL指令支持泛型操作。而真正的泛型實例化工作以"on-demand"的方式,發生在JIT編譯時。
1. 第一輪編譯時,編譯器只為Stack(棧演演算法)類型產生“泛型版”的IL代碼與元數據-----並不進行泛型類型的實例化,T在中間只充當佔位符
2. JIT編譯時,當JIT編譯器第一次遇到Stack時,將用int替換“泛型版”IL代碼與元數據中的T---進行泛型類型的實例化。CLR為所有類型參數為“引用類型”的泛型類型產生同一份代碼;但如果類型參數為“值類型”,對每一個不同的“值類型”,CLR將為其產生一份獨立的代碼。