新聞中心
【1】:泛型介紹
創(chuàng)新互聯(lián)成立于2013年,先為旺蒼等服務(wù)建站,旺蒼等地企業(yè),進(jìn)行企業(yè)商務(wù)咨詢服務(wù)。為旺蒼企業(yè)網(wǎng)站制作PC+手機(jī)+微官網(wǎng)三網(wǎng)同步一站式服務(wù)解決您的所有建站問題。泛型是C#2.0中一個(gè)重要的新特性,泛型是CLR和編程語言提供的一種特殊機(jī)制,它支持另一種形式的代碼重用。
泛型誕生之前:
C#是一種強(qiáng)類型的語言,在沒有泛型沒有被提出之前,我們?cè)谑褂眉系拇a的時(shí)候,每次對(duì)其進(jìn)行轉(zhuǎn)換都需要隱式的強(qiáng)制轉(zhuǎn)換,我們都知道所有對(duì)象的最終基類是object,我們?cè)诿看问褂胦bject的時(shí)候,無論是變換什么類型都要對(duì)其進(jìn)行強(qiáng)制轉(zhuǎn)換。
一般情況下,創(chuàng)建泛型類的過程為:從一個(gè)現(xiàn)有的具體類開始,逐一將每個(gè)類型更改為類型參數(shù),直至達(dá)到通用化和可用性的最佳平衡。 創(chuàng)建您自己的泛型類時(shí),需要特別注意以下事項(xiàng):
將哪些類型通用化為類型參數(shù)。
通常,能夠參數(shù)化的類型越多,代碼就會(huì)變得越靈活,重用性就越好。 但是,太多的通用化會(huì)使其他開發(fā)人員難以閱讀或理解代碼。
如果存在約束,應(yīng)對(duì)類型參數(shù)應(yīng)用什么約束
一條有用的規(guī)則是,應(yīng)用盡可能最多的約束,但仍使您能夠處理必須處理的類型。 例如,如果您知道您的泛型類僅用于引用類型,則應(yīng)用類約束。 這可以防止您的類被意外地用于值類型,并允許您對(duì) T 使用 as 運(yùn)算符以及檢查空值。
是否將泛型行為分解為基類和子類。
由于泛型類可以作為基類使用,此處適用的設(shè)計(jì)注意事項(xiàng)與非泛型類相同。 請(qǐng)參見本主題后面有關(guān)從泛型基類繼承的規(guī)則。
是否實(shí)現(xiàn)一個(gè)或多個(gè)泛型接口。
例如,如果您設(shè)計(jì)一個(gè)類,該類將用于創(chuàng)建基于泛型的集合中的項(xiàng),則可能必須實(shí)現(xiàn)一個(gè)接口,如 IComparable,其中 T 是您的類的類型。
【2】:泛型的表示方式
泛型可以為引用類型和值類型還有接口和委托,F(xiàn)CL( DLL程序集包含了.NET框架下的各種DLL )類中定義了一個(gè)泛型列表,用來管理一個(gè)對(duì)象集合,如果我們想要使用這個(gè)泛型列表,可以在使用時(shí)指定具體數(shù)據(jù)類型。
泛型的表示為 “T” 如:List
【3】:泛型的好處
1 : 使代碼更加的簡潔,清晰
從前面我們也提到了,泛型具備可重用性 , 減少我們代碼量, 使我們的程序更容易開發(fā)和維護(hù),舉例 :
舉例 1 : 在從數(shù)據(jù)庫中獲取數(shù)據(jù)庫的時(shí)候,我們經(jīng)常會(huì)返回一個(gè)DataTable類型,然后將其轉(zhuǎn)換為List集合. 那么如果沒有泛型的話,我會(huì)一般會(huì)這樣來做.
public ListGetTrainingUser(string userId) { DataTable dt = SqliteHelper.ExecuteDataset(System.Data.CommandType.Text, @" SELECT DISTINCT UserId,TrainingId FROM TRAININGUSER AS TU INNER JOIN [USER] AS U ON U.ID = TU.USERID JOIN [TRAINING] AS T ON T.ID = TU.TRAININGID WHERE U.ID = '"+userId+"' AND T.ENDTIME > DATETIME('now', 'localtime') AND T.StartTime <= DATETIME('now', 'localtime') ;").Tables[0]; return DataTableToList(dt); }
private ListDataTableToList(DataTabledt) { List list = new List (); if(dt. Rows.Count > 0 ) { foreach (DataRow row in dt .Rows) { TrainingUser trainingUser = new TrainingUser(); if(row["UserId" ] != null) { trainingUser .UserId = row["UserId"].ToString(); } if(row["TrainingId" ] != null) { trainingUser.TrainingId = row["TrainingId"].ToString(); } list.Add(trainingUser); } } return list; }
在方法DataTableToList中,我們傳入了一個(gè)DataTable的對(duì)象,然后在去循環(huán)遍歷每一行的對(duì)象值從而去賦值給TrainingUser類型對(duì)象,這只是其中的一個(gè)方法,如果我們的類型還有 Training/User/Project等類型的話,我們是不是就要寫很多如同DataTableToList這樣的方法呢? 這就體現(xiàn)出了這樣的方式,會(huì)造成代碼的冗余以及維護(hù)不便問題,那么我們使用泛型來解決
舉例 2 : 使用泛型使上面的代碼更見的清晰,簡潔
public static ListToList1 (DataTable dt) whereT : class, new() { var prlist =new List (); Type type = typeof(T); Array.ForEach( type.GetProperties(), p => { if(dt.Columns.IndexOf(p.Name) !=-1) { prlist.Add(p); } }); var oblist = new List (); // System.Data.SqlTypes. foreach(DataRow row in dt.Rows) { var ob = new T(); prlist.ForEach( p => { if(row[p.Name] != DBNull.Value) { p.SetValue(ob, row[p.Name], null); } }); oblist.Add(ob); } return oblist; }
在上面的這個(gè)方法中,我們定義了一個(gè)泛型方法,內(nèi)部實(shí)現(xiàn)中是使用了反射的原理,將DataTable轉(zhuǎn)換為了List(反射后續(xù)隨筆中總結(jié),此處只關(guān)注泛型部分即可),我們定義了一個(gè)靜態(tài)的返回值為List
public ListGetTrainingIdByUserId(string userId) { List trainingUserList = DataTableHelper.ToList1 ( SqliteHelper.ExecuteDataset(System.Data.CommandType.Text, @" SELECT DISTINCT UserId,TrainingId FROM TRAININGUSER AS TU INNER JOIN [USER] AS U ON U.ID = TU.USERID JOIN [TRAINING] AS T ON T.ID = TU.TRAININGID WHERE U.ID = '"+ userId +"' AND T.ENDTIME > DATETIME('now', 'localtime') AND T.StartTime <= DATETIME('now', 'localtime') ;").Tables[0]); return trainingUserList ; }
代碼中的DataTableHelper.ToList1
這樣即便我們后續(xù)還有Training/User/Project等其他的類型,我們都可以直接使用DataTableHelper.ToList1
2 : 提升程序的性能
泛型與非泛型相比較而言,性能要好一些,這是為什么? 首先,泛型的類型是由調(diào)用者(接收者),去直接賦值的(類型安全的), 那么就不會(huì)存在類型轉(zhuǎn)換的問題,其次, 泛型減少了裝箱和拆箱的過程.
實(shí)例 3 : 對(duì)于值類型泛型與非泛型的性能比較
private static void ListTest() { Listlist = new List (); for(inti = 0; i < 100; i++) { list.Add(i); int a = list[i]; } list =null; } private static void ArrListTest() { ArrayList arr = new ArrayList(); for(inti = 0; i <100; i++) { arr.Add(i); int s = (int)arr[i]; } arr = null; }
Stopwatch sw = new Stopwatch(); sw.Start(); ListTest(); Console.WriteLine(" 使用泛型List執(zhí)行值類型方法歷時(shí) : "+ sw.Elapsed.ToString()); sw.Stop(); Stopwatch sw1 = new Stopwatch(); sw1.Start(); ArrListTest(); Console.WriteLine(" 使用非泛型ArrayList執(zhí)行值類型方法歷時(shí) : "+ sw1.Elapsed.ToString()); sw1.Stop(); Console.ReadLine();
通過循環(huán) 100 來比較,結(jié)果為 :
我們可以看到非泛型的時(shí)間要比泛型的時(shí)間多出0.0000523秒,泛型比非泛型的時(shí)間要多出一些, 那么我們將數(shù)值改動(dòng)一下改為循環(huán) 1000次.得出結(jié)果為 :
泛型比非泛型執(zhí)行的時(shí)間要短0.0000405秒
我們將時(shí)間在改動(dòng)一下,改為 100000呢?結(jié)果為 :
這次差距為 0.0054621 并且隨著執(zhí)行次數(shù)的增長,非泛型相比泛型的時(shí)間會(huì)逐步的增加,
通過反編譯我們也能看出 :
泛型:
非泛型
從編譯中我們也能看出泛型方法中,接收的為Int32,非泛型為Object,其次泛型不會(huì)進(jìn)行裝箱和拆箱操作,非泛型每次執(zhí)行都要進(jìn)行裝箱和拆箱操作.
3 : 類型安全
在實(shí)例1 , 2 ,3 中我們都有備注說明,泛型的發(fā)送著必須要和接收者進(jìn)行一致,否則會(huì)報(bào)異常 ,例如 :
實(shí)例 4 :
講一個(gè)泛型算法應(yīng)用于一個(gè)具體的類型時(shí),編譯器和CLR能理解開發(fā)人員的意圖,并保證只有與指定數(shù)據(jù)類型兼容的對(duì)象才能隨同算法使用,若試圖使用不兼容類型的一個(gè)對(duì)象,會(huì)造成編譯時(shí)錯(cuò)誤,或者運(yùn)行時(shí)拋出異常,
在值類型中,泛型要比非泛型的性能高出很多,在引用類型中,泛型和非泛型的性能相差無幾,但是代碼簡潔了很多。
此篇至此,下篇主要知識(shí)點(diǎn) :
1、泛型接口
2、泛型委托
3、泛型約束(主要約束,次要約束,構(gòu)造器約束)
4、泛型類型轉(zhuǎn)型
5、泛型和反射
6、泛型和屬性
參考資料 :
CLR C# Via
深入理解C#
https://msdn.microsoft.com/zh-cn/library/sz6zd40f.aspx
https://msdn.microsoft.com/zh-cn/library/0x6a29h7.aspx
如果你覺得本文對(duì)你有幫助的話,請(qǐng)點(diǎn)右下角的推薦,或者直接關(guān)注我,后續(xù)將不斷更新.NET解析這一系列的文章....
溫馨提示 : 知識(shí)點(diǎn)重溫,不斷總結(jié),思考, 也是一種階段性提高,希望能幫到在讀的你.
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無理由+7*72小時(shí)售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務(wù)可用性高、性價(jià)比高”等特點(diǎn)與優(yōu)勢(shì),專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場(chǎng)景需求。
網(wǎng)頁名稱:.NET泛型解析(上)-創(chuàng)新互聯(lián)
當(dāng)前地址:http://biofuelwatch.net/article/cdiics.html