C# 泛型基礎(chǔ)知識學(xué)習(xí)大全
C# 泛型是C# 2.0中最強大的功能。特點一:通過泛型可以定義類型安全的數(shù)據(jù)結(jié)構(gòu),而無需使用具體實際的數(shù)據(jù)類型,著能夠顯著提高性能并得到高質(zhì)量的代碼。在概念上,反省類似于C++模板,但是在實現(xiàn)和功能方面存在明顯差異。特點二:泛型通過把類型參數(shù)化來達到代碼重用的目標,這一特性可以應(yīng)用在類、結(jié)構(gòu)、接口、委托、方法的設(shè)計之中。泛型除了可以大幅提高代碼復(fù)用性外,特點三:還可以提供編譯期間的類型檢查,減少不必要的顯式類型轉(zhuǎn)換,減少不必要的裝箱操作,從而提高應(yīng)用程序的運行效率。
1. 引入C# 泛型的原因:
一般情況下,在通用的數(shù)據(jù)結(jié)構(gòu)中(例如Stack,List,Dictionory等)存儲的數(shù)據(jù),要求必須有相同的數(shù)據(jù)類型。如果必須存儲不同類型的數(shù)據(jù),那么唯一的方法就是將所有的數(shù)據(jù)首先裝箱為object 類型,然后再存儲。
例如,下面的Stack類將其所有的數(shù)據(jù)存儲在一個object類型的數(shù)組中,該類型的兩個方法分別使用object來獲取和返回數(shù)據(jù):
- public class Stack
 - {
 - object[] items;
 - public void Push(object item) {...}
 - public object Pop() {...}
 - }
 
這樣做的缺點:
第一是性能:根據(jù)裝箱和堆棧的功能,使用Push方法能夠想堆棧中壓入任何類型的值,然而,再重新獲取堆棧中的數(shù)據(jù)值時必須在使用Pop方法拆箱的同時,使用顯式類型轉(zhuǎn)換得到合適的數(shù)據(jù)類型。這種裝箱和拆箱的操作增加了執(zhí)行的負擔(dān),因為它帶來了動態(tài)內(nèi)存分配和運行時類型檢查。
第二是類型安全。因為編譯器允許在任何類型和Object之間進行強制類型轉(zhuǎn)換,所以將造成編譯時類型安全的不足。主要是Stack類無法強制設(shè)置堆棧中的數(shù)據(jù)類型。
第三是工作效率。編寫類型特定的數(shù)據(jù)結(jié)構(gòu)以及冗繁的的轉(zhuǎn)換代碼是一項乏味重復(fù)的且易于出錯的工作 。
為了有效解決以上問題,c#2.0引入了泛型。
2.C# 泛型概述
泛型常見于集合應(yīng)用中,在。net2.0框架的類庫中,提供了一個新的名空間System.Collections.Generic,其中包含了一些新的基于泛型的容器類,例如,System.Collectinos.Generic.Stack,System.Collections.Dictionary,Collections.Generic.List,System.Collections.Generic.Queue
等,這些類庫可以在集合中實現(xiàn)泛型。
以堆棧中實現(xiàn)泛型為例
Stack類示意代碼:
- public class Stack﹤T﹥
 - {
 - T[] items;
 - int count;
 - public void Push(T item) {...}
 - public T Pop() {...}
 - }
 
Stack類應(yīng)用代碼:
- //實例化Stack類
 - Ststem.Collections.Genric.Stack﹤string﹥ stringStack = new System.Collections.Generic.Stack﹤string﹥;
 - //增加數(shù)據(jù)
 - stringStack.Push("硬盤");
 - stringStack.Push("聲卡");
 - stringStack.Push("電源");
 - //轉(zhuǎn)換為數(shù)組
 - Araay stringArray;
 - stringArray = stringStack.ToAraay();
 - //顯示數(shù)據(jù)
 - foreach(string item in stringArray)
 - {
 - Console.WriteLine(item);
 - }
 
3.C# 泛型之約束
為了提供更強大的編譯期間的類型檢查和減少類型轉(zhuǎn)換,c#允許一個可選的為每個類型參數(shù)提供的約束列表。一個類型參數(shù)的約束指定了一個類型必須遵守的要求,使得這個類型參數(shù)能夠作為一個變量來使用。約束有關(guān)鍵字where來聲明,后跟類型參數(shù)的名字,再后是一個類或接口 類型的列表,或者構(gòu)造器約束new().
比如要想使Dictionary﹤K,V﹥類能保證鍵值始終實現(xiàn)IComparable接口,類的聲明中應(yīng)該對類型參數(shù)K指定一個約束
- public class Dictionary﹤K,V﹥ where K: IComparable
 - {
 - public void Add(K key, V value)
 - {
 - ...
 - if(key.CompareTo(x) ﹤ 0 ) {....}
 - ...
 - }
 - }
 
類型參數(shù)約束的使用要小心。盡管它們提供了更強大的編譯期間的類型檢查并在一些情況下改進了性能,它還是限制了泛型類型的使用。比如一個泛型類List﹤T﹥可能約束T實現(xiàn)IComparable接口,以便Sort方法能夠比較其中的元素。然而這么做的結(jié)果使得List﹤T﹥不能用于那些不能實現(xiàn)IComparable接口的類型,盡管在某些情況下Sort方法從沒有被實際調(diào)用過。
4.C# 泛型類型的成員
- class C﹤V﹥
 - {
 - public V f1; //聲明字段
 - public D﹤V﹥ f2; //作為其他泛型類型 的參數(shù)
 - public C﹤V x﹥
 - {
 - this.f1 = x;
 - }
 - }
 
泛型類型的成員可以使用泛型類型聲明中的類型參數(shù)。但類型參數(shù)如果沒有任何約束,則只能在該類型上使用從System.Object繼承的共有成員。
5.C# 泛型接口
- interface IList﹤T﹥
 - {
 - T[] GetElements();
 - }
 - interface IDictionary﹤K,V﹥
 - {
 - void Add(K key,V value);
 - }
 - //泛型接口的類型參數(shù)要么已實例化
 - //要么來源于實現(xiàn)類聲明的類型參數(shù)
 - class List﹤T﹥:IList﹤T﹥,IDictionary﹤int,T﹥
 - {
 - public T[] GetElements{}
 - {
 - return null;
 - }
 - public void Add(int index,T value){}
 - }
 
6.C# 泛型委托
- delegate bool Predicate﹤T﹥(T value);
 - class X
 - {
 - static bool F(int i){...}
 - static bool G(string s){...}
 - static void Main()
 - {
 - Predicate﹤string﹥ p2 = G;
 - Predicate﹤int﹥ p1 = new Predicate﹤int﹥(F);
 - }
 - }
 
泛型委托支持返回值和參數(shù)哂納感應(yīng)用參數(shù)類型,這些參數(shù)類型同樣可以附帶合法的約束。
7.C# 泛型方法的簡介
C#泛型機制只支持“在方法聲明上包含類型參數(shù)” -- 即泛型方法。
C#泛型機制不支持在除方法外的其他成員(包括屬性、事件、索引器、構(gòu)造器、析構(gòu)器)的聲明上包含類型參數(shù),但這些成員本身可以包含在泛型類型中,并使用泛型類型的類型參數(shù)。
泛型方法既可以包含在泛型類型中,也可以包含在非泛型類型中。
8.C# 泛型方法的聲明與調(diào)用
- public class Finder
 - {
 - // 泛型方法的聲明
 - public static int Find﹤T﹥(T[] items,T item)
 - {
 - for(int i=0;i﹤items.Length;i++)
 - {
 - if(items[i].Equals(item)
 - {
 - return i;
 - }
 - }
 - return -1;
 - }
 - }
 - // 泛型方法的調(diào)用
 - int i = Finder.Find﹤T﹥(new int[]{1,3,4,5,6,8,9},6);
 
泛型編程
9.C# 泛型方法的重載
- class MyClass
 - {
 - void F1﹤T﹥(T[] a,int i); // 不可以構(gòu)成重載方法
 - void F1﹤U﹥(U[] a,int i);
 - void F2﹤T﹥(int x); // 可以構(gòu)成重載方法
 - void F2(int x);
 - void F3﹤T﹥(T t) where T : A; // 不可以構(gòu)成重載方法
 - void F3﹤T﹥(T t) where T : B;
 - }
 
10.C# 泛型方法的重寫
- abstract class Base
 - {
 - public abstract T F﹤T,U﹥(T t,U u) where U : T;
 - public abstract T G﹤T﹥(T t) where U : IComparable;
 - }
 - class Derived:Base
 - {
 - // 合法的重寫,約束被默認繼承
 - public override X F(X,Y)(X x,Y y){}
 - // 非法的重寫,指定任何約束都是多余的
 - public override T G﹤T﹥(T t) where T : Comparable{}
 - }
 
11.C# 泛型約束簡介
C#泛型要求對"所有泛型類型或泛型方法的類型參數(shù)"的任何假定,都要基于"顯式的約束",以維護C#所要求的類型安全.
"顯式約束"有where字句表達,可以指定"基類約束","接口約束","構(gòu)造器約束","值類型/引用類型約束"共四中約束.
"顯示約束"并非必須,如果沒有指定"顯式約束",泛型類型參數(shù)將只能訪問System.Object類型中的公有方法.
基類約束
- class A
 - {
 - public void F1(){}
 - }
 - class B
 - {
 - public void F2(){}
 - }
 - class C(S,T)
 - where S:A // S繼承自A
 - where T:B // T繼承自B
 - {
 - // 可以在類型為S的變量上調(diào)用F1
 - // 可以在類型為T的變量上調(diào)用F2
 - }
 
接口約束
- interface IPrintable{coid Print();}
 - interface IComparable﹤T﹥{int CompareTo(T v);}
 - interface IKeyProvider﹤T﹥{T HetKey();}
 - class Dictionary﹤K,V﹥
 - where K:IComparable﹤K﹥
 - where V:IPrintable,IKeyProvider﹤K﹥
 - {
 - // 可以在類型為K的變量上調(diào)用CompareTo
 - // 可以在類型為V的變量上調(diào)用Print和GetKey
 - }
 
構(gòu)造器約束
- class A
 - {
 - public A(){}
 - }
 - class B
 - {
 - public B(int i)()
 - }
 - class C﹤T﹥
 - where T:new()
 - {
 - // 可以在其中使用T t = new T();
 - }
 - C﹤A﹥ c = new C﹤A﹥(); // 可以,A有無參數(shù)構(gòu)造器
 - C﹤B﹥ c = new C﹤B﹥(); // 錯誤,B沒有無參數(shù)構(gòu)造器
 
值類型/引用類型約束
- public struct A{...}
 - public class B{...}
 - class C﹤T﹥
 - where T : struct
 - {
 - // T在這里面是一個值類型
 - }
 - C﹤A﹥ c = new C﹤A﹥(); // 可以,A是一個值類型
 - C﹤B﹥ c = new C﹤B﹥(); // 錯誤,B是一個引用類型
 
12.C# 泛型總結(jié)
C# 泛型能力有CLR在運行時支持,它既不同于c++在編譯時所支持的靜態(tài)模板,也不同于java在編譯器層面使用"檫拭法"支持的簡單的類型.
C# 泛型支持包括類,結(jié)構(gòu),接口,委托共四種泛型類型,以及方法成員.
C# 泛型采用"基類,接口,構(gòu)造器,值類型/引用類型"的約束方式來實現(xiàn)對類型參數(shù)的"顯式約束",它不支持C++模板那樣的基于簽名的顯式約束.
C# 泛型的基本知識就介紹到這里了,希望對你了解和學(xué)習(xí)C# 泛型有所幫助。
【編輯推薦】















 
 
 
 
 
 
 