淺談C#泛型的用處

來源:互聯網
上載者:User
為什麼要使用C#泛型?

為了瞭解這個問題,我們先看下面的代碼,代碼省略了一些內容,但功能是實現一個棧,這個棧只能處理int資料類型:

public class Stack { PRivate int[] m_item; public int Pop(){...} public void Push(int item){...} public Stack(int i) { this.m_item = new int[i]; } } 上面代碼啟動並執行很好,但是,當我們需要一個棧來儲存string類型時,該怎麼辦呢?很多人都會想到把上面的代碼複製一份,把int改成string不就行了。當然,這樣做本身是沒有任何問題的,但一個優秀的程式是不會這樣做的,因為他想到若以後再需要long、Node類型的棧該怎樣做呢?還要再複製嗎?優秀的程式員會想到用一個通用的資料類型object來實現這個棧:

public class Stack { private object[] m_item; public object Pop(){...} public void Push(object item){...} public Stack(int i) { this.m_item = new[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; public T Pop(){...} public void Push(T item){...} public Stack(int i) { this.m_item = new T[i]; } } 類的寫法不變,只是引入了通用資料類型T就可以適用於任何資料類型,並且型別安全的。這個類的調用方法:

//執行個體化只能儲存int類型的類 Stack a = new Stack(100); a.Push(10); a.Push("8888"); //這一行編譯不通過,因為類a只接收int類型的資料 int x = a.Pop(); //執行個體化只能儲存string類型的類 Stack b = new Stack(100); b.Push(10); //這一行編譯不通過,因為類b只接收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#泛型的用處的內容,更多相關內容請關注topic.alibabacloud.com(www.php.cn)!

  • 相關文章

    聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.