為什麼要使用C#泛型?
為了了解這個問題,我們先看下面的程式碼,程式碼省略了一些內容,但功能是實作一個棧,這個棧只能處理int資料型別:
public class Stack { PRivate int[] m_item; public int Pop(){...} public void Push(int item){ this.m_item = new int[i]; } } 以上碼執行的很好,但是,當我們需要一個堆疊來保存string類型時,該怎麼辦呢?很多人都會想到把上面的程式碼複製一份,把int改成string不就行了。當然,這樣做本身是沒有任何問題的,但一個優秀的程式是不會這樣做的,因為他想到若以後再需要long、Node類型的棧該怎麼做呢?還要再複製嗎?優秀的程式設計師會想到用一個通用的資料型別object來實現這個堆疊:
public class Stack { private public void Push(object item){.. .} public Stack(int i) { } 這個棧寫的不錯,他非常靈活,可以接收任何資料類型,可以說是一勞永逸。但全面地講,也不是沒有缺陷的,主要表現在:
當Stack處理值類型時,會出現裝箱、折箱操作,這將在託管堆上分配和回收大量的變量,若數據量大,則性能損失非常嚴重。
在處理引用類型時,雖然沒有裝箱和折箱操作,但將用到資料型別的強制轉換操作,增加處理器的負擔。
在數據類型的強制轉換上還有更嚴重的問題(假設stack是Stack的一個實例):
Node1 x = new Node1(); stack.Push(x); Node2 y = (Node2)stack. Pop(); 上面的程式碼在編譯時是完全沒問題的,但由於Push了一個Node1類型的數據,但在Pop時卻要求轉換為Node2類型,這將出現程式運行時的類型轉換異常,但卻逃離了編譯器的檢查。
針對object類型棧的問題,我們引入泛型,他可以優雅地解決這些問題。泛型用用一個通過的資料類型T來取代object,在類別實例化時指定T的類型,運行時(Runtime)自動編譯為本地程式碼,運行效率和程式碼品質都有很大提高,並且保證資料類型安全。
使用C#泛型
下面是用泛型來重寫上面的棧,用一個通用的資料型別T來作為一個佔位符,等待在實例化時用一個實際的型別來代替。讓我們來看看泛型的威力:
public class Stack { private T[] m_item item){...} public Stack(int i) { this.m_item = new T[i]; this.m_item = new T[i]; } } 類別的寫法不變,只是引入了通用資料類型T這個類別的呼叫方法:
//實例化只能保存int類型的類別 Stack a = new Stack(100); a.Push(10); a.Push("8888"); //這一行中只接收未接收,因為類別的編譯類型的資料 int x = a.Pop(); //實例化只能保存string類型的類別 Stack b = new Stack(100); b.Push(10) //此行編譯不通過,因為此行編譯只接收string類型的資料 b.Push("8888"); string y = b.Pop(); 這個類別和object實現的類別有截然不同的差異:
1. 他是類型安全的。實例化了int型別的棧,就不能處理string類型的數據,其他資料型別也一樣。
2.無需裝箱和折箱。這個類別在實例化時,按照所傳入的資料類型產生本地代碼,本地代碼資料類型已確定,所以無需裝箱和折箱。
3. 無需型別轉換。
理論知識:
所謂泛型:即透過參數化類型來實現在同一份程式碼上操作多種資料型別。泛型程式設計是一種程式設計範式,它利用「參數化類型」將類型抽象化,從而實現更靈活的複用。
C#泛型賦予了程式碼更強的型別安全,更好的複用,更高的效率,更清晰的限制。
C#泛型能力由CLR在運行時支持,區別於C++的編譯時模板機制,和java的編譯時的「搽拭法」。這使得泛型能力可以在各個支援CLR的語言之間進行無縫的互通。
C#泛型程式碼在被編譯為IL和元資料時,採用特殊的佔位符來表示泛型類型,並用專有的IL指令支援泛型運算。而真正的泛型實例化工作以「on-demand」的方式,發生在JIT編譯時。
C#泛型編譯機制如下:
第一輪編譯時,編譯器只為Stack類型產生「泛型版」的IL程式碼和元數據,並不進行泛型類型的實例化,T在中間只充當佔位符。
JIT編譯時,當JIT編譯器第一次遇到Stack時,將以int型別取代「泛型版」IL程式碼與元資料中的T -- 進行泛型類型的實例化。
CLR為所有類型參數為“引用類型”的泛型類型產生同一份代碼,但如果類型參數為“值類型”,對每一個不同的“值類型”,CLR將為其產生一份獨立的代碼。
C#泛型的幾個特點
如果實例化泛型類型的參數相同,那麼JIT編譯器會重複使用該類型,因此C#的動態泛型能力避免了C++靜態模板可能導致的程式碼膨脹的問題。
C#泛型類型攜帶有豐富的元數據,因此C#的泛型類型可以應用於強大的反射技術。
C#的泛型採用“基類、接口、構造器、值類型/引用類型”的約束方式來實現對類型參數的“顯示約束”,提高了類型安全的同時,也喪失了C++模板基於“簽名」的隱式約束所具有的高彈性。
C#泛型類別在編譯時,先生成中間程式碼IL,通用型別T只是一個佔位符。在實例化類別時,根據使用者指定的資料類型取代T並由即時編譯器(JIT)產生本地程式碼,這個本地程式碼中已經使用了實際的資料類型,等同於用實際類型寫的類,所以不同的封閉類別的本地代碼是不一樣的。依照這個原理,我們可以這樣認為:泛型類別的不同的封閉類別是分別不同的資料類型。
這樣泛型不僅更靈活,也同時將程式碼的簡單和提升到一個層次!不用再為具體不同的重載方法寫出具體的程式碼了!
C# 泛型是開發工具庫中的一個無價之寶。它們可以提高效能、類型安全性和質量,減少重複性的程式設計任務,簡化整體程式設計模型,而這一切都是透過優雅的、可讀性強的語法完成的。儘管 C# 泛型的根基是 C++ 模板,但 C# 透過提供編譯時安全性和支援將泛型提高到了一個新水平。 C# 利用了兩階段編譯、元資料以及諸如約束和一般方法之類的創新性的概念。毫無疑問,C# 的將來版本將繼續發展泛型,以便添加新的功能,並且將泛型擴展到諸如數據訪問或本地化之類的其他.NET Framework 領域.
以上就是淺談C#泛型的用處的內容,更多相關內容請關注PHP中文網(www.php.cn)!