.NET泛型解析
【1】:泛型介紹
泛型是C#2.0中一個重要的新特性,泛型是CLR和程式語言提供的一種特殊機制,它支援另一種形式的程式碼重用。泛型通常用與集合以及作用於集合的方法一起使用,當然也可以單獨使用.
C#是一種強類型的語言,在泛型沒有被提出之前,我們在使用集合的代碼的時候,每次對其進行轉換都需要隱式的強制轉換,我們都知道所有物件的最終基底類別是object,我們在每次使用object的時候,無論是變換什麼類型都要對其進行強制轉換。
那麼有了泛型之後,使用泛型我們就無需進行轉換,因為泛型根據接收者傳入的參數類型,直接將每個類型更改為所傳入的參數類型.
一般情況下,創建泛型類的過程為:從一個現有的特定類別開始,逐一將每個類型變更為類型參數,直到達到通用化和可用性的最佳平衡。 建立您自己的泛型類別時,需要特別注意以下事項:
將哪些類型通用化為類型參數。
通常,能夠參數化的類型越多,程式碼就會變得越靈活,重複使用性就越好。 但是,太多的通用化會使其他開發人員難以閱讀或理解程式碼。
如果存在約束,應對類型參數應用什麼約束
一條有用的規則是,應用盡可能最多的約束,但仍使您能夠處理必須處理的類型。 例如,如果您知道您的泛型類別僅用於引用類型,則套用類別約束。 這可以防止您的類別意外地用於值類型,並允許您對 T 使用 as 運算子以及檢查空值。
是否將泛型行為分解為基底類別和子類別。
由於泛型類別可以作為基底類別使用,此處適用的設計注意事項與非泛型類別相同。 請參閱本主題後面有關從泛型基底類別繼承的規則。
是否實作一個或多個泛型介面。
例如,如果您設計一個類,該類將用於創建基於泛型的集合中的項,則可能必須實現一個接口,如 IComparable,其中 T 是您的類的類型。
【2】:泛型的表示方式
泛型可以為引用類型和值類型還有介面和委託,FCL( DLL程式集,包含了.NET框架下的各種DLL )類別中定義了一個泛型列表,用來管理一個物件集合,如果我們想要使用這個泛型列表,可以在使用時指定具體資料類型。
泛型的表示為「T」 如:List
System.Collections.Generic 命名空間包含定義泛型集合的介面和類,泛型集合允許使用者創建強類型集合,它能提供比非泛型強類型集合更好的類型安全性和效能。
創建泛型類別的過程為:從一個現有的具體類別開始,逐一將每個類型更改為類型參數,直至達到通用化和可用性的最佳平衡
【3】:泛型的好處
1 : 使程式碼更加的簡潔,清晰
從前面我們也提到了,泛型具備可重用性, 減少我們代碼量, 使我們的程序更容易開發和維護,舉例:
實例1 : 在從資料庫中取得資料庫的時候,我們常常會回傳一個DataTable類型,然後將其轉換為List集合. 那麼如果沒有泛型的話,我會一般會這樣來做.
public List<TrainingUser>GetTrainingUser(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 List<TrainingUser> DataTableToList(DataTabledt) { List<TrainingUser> list = new List<TrainingUser>(); 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中,我們傳入了一個DataTable的物件,然後在去循環遍歷每一行的物件值從而去賦值給TrainingUser類型物件,這只是其中的一個方法,如果我們的類型還有Training/User/Project等類型的話,我們是不是就要寫很多如同DataTableToList這樣的方法呢? 這就體現出了這樣的方式,會造成代碼的冗餘以及維護不便問題,那麼我們使用泛型來解決
實例2 : 使用泛型使上面的代碼更見的清晰,簡潔
public static List<T> ToList1<T>(DataTable dt) whereT : class, new() { var prlist =new List<PropertyInfo>(); Type type = typeof(T); Array.ForEach( type.GetProperties(), p => { if(dt.Columns.IndexOf(p.Name) !=-1) { prlist.Add(p); } }); var oblist = new List<T>(); // 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; }
在上面的這個方法中,我們定義了一個泛型方法,內部實現中是使用了反射的原理,將DataTable轉換為了List(反射後續隨筆中總結,此處只關注泛型部分即可),我們定義了一個靜態的回傳值為List
public List<TrainingUser>GetTrainingIdByUserId(string userId) { List<TrainingUser> trainingUserList = DataTableHelper.ToList1<TrainingUser>( 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
这样即便我们后续还有Training/User/Project等其他的类型,我们都可以直接使用DataTableHelper.ToList1
2 : 提升程序的性能
泛型与非泛型相比较而言,性能要好一些,这是为什么? 首先,泛型的类型是由调用者(接收者),去直接赋值的(类型安全的), 那么就不会存在类型转换的问题,其次, 泛型减少了装箱和拆箱的过程.
实例 3 : 对于值类型泛型与非泛型的性能比较
private static void ListTest() { List<int>list = new List<int>(); 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执行值类型方法历时 : "+ sw.Elapsed.ToString()); sw.Stop(); Stopwatch sw1 = new Stopwatch(); sw1.Start(); ArrListTest(); Console.WriteLine(" 使用非泛型ArrayList执行值类型方法历时 : "+ sw1.Elapsed.ToString()); sw1.Stop(); Console.ReadLine();
通过循环 100 来比较,结果为 :
我们可以看到非泛型的时间要比泛型的时间多出0.0000523秒,泛型比非泛型的时间要多出一些, 那么我们将数值改动一下改为循环 1000次.得出结果为 :
泛型比非泛型执行的时间要短0.0000405秒
我们将时间在改动一下,改为 100000呢?结果为 :
这次差距为 0.0054621 并且随着执行次数的增长,非泛型相比泛型的时间会逐步的增加,
通过反编译我们也能看出 :
泛型:
非泛型
从编译中我们也能看出泛型方法中,接收的为Int32,非泛型为Object,其次泛型不会进行装箱和拆箱操作,非泛型每次执行都要进行装箱和拆箱操作.
3 : 类型安全
在实例1 , 2 ,3 中我们都有备注说明,泛型的发送着必须要和接收者进行一致,否则会报异常 ,例如 :
实例 4 :
首页
最新文章
IT 职场
前端
后端
移动端
数据库
运维
其他技术
- 导航条 -首页最新文章IT 职场前端- JavaScript- HTML5- CSS后端- Python- Java- C/C++- PHP- .NET- Ruby- Go移动端- Android- iOS数据库运维- Linux- UNIX其他技术- Git- 机器学习- 算法- 测试- 信息安全- Vim
伯乐在线 > 首页 > 所有文章 > 开发 > .NET泛型解析(上)
.NET泛型解析(上)
2015/07/03 · 开发 · .Net
分享到:6
拍摄与剪辑“怀孕日记”
PS大神通关教程
PS入门基础-魔幻调色
慕课网技术沙龙之前端专场
原文出处: 刘彬的博客
【1】:泛型介绍
泛型是C#2.0中一个重要的新特性,泛型是CLR和编程语言提供的一种特殊机制,它支持另一种形式的代码重用。泛型通常用与集合以及作用于集合的方法一起使用,当然也可以单独使用.
C#是一种强类型的语言,在泛型没有被提出之前,我们在使用集合的代码的时候,每次对其进行转换都需要隐式的强制转换,我们都知道所有对象的最终基类是object,我们在每次使用object的时候,无论是变换什么类型都要对其进行强制转换。
那么有了泛型之后,使用泛型我们就无需进行转换,因为泛型根据接收者传入的参数类型,直接将每个类型更改为所传入的参数类型.
一般情况下,创建泛型类的过程为:从一个现有的具体类开始,逐一将每个类型更改为类型参数,直至达到通用化和可用性的最佳平衡。 创建您自己的泛型类时,需要特别注意以下事项:
将哪些类型通用化为类型参数。
通常,能够参数化的类型越多,代码就会变得越灵活,重用性就越好。 但是,太多的通用化会使其他开发人员难以阅读或理解代码。
如果存在约束,应对类型参数应用什么约束
一条有用的规则是,应用尽可能最多的约束,但仍使您能够处理必须处理的类型。 例如,如果您知道您的泛型类仅用于引用类型,则应用类约束。 这可以防止您的类被意外地用于值类型,并允许您对 T 使用 as 运算符以及检查空值。
是否将泛型行为分解为基类和子类。
由于泛型类可以作为基类使用,此处适用的设计注意事项与非泛型类相同。 请参见本主题后面有关从泛型基类继承的规则。
是否实现一个或多个泛型接口。
例如,如果您设计一个类,该类将用于创建基于泛型的集合中的项,则可能必须实现一个接口,如 IComparable,其中 T 是您的类的类型。
【2】:泛型的表示方式
泛型可以为引用类型和值类型还有接口和委托,FCL( DLL程序集,包含了.NET框架下的各种DLL )类中定义了一个泛型列表,用来管理一个对象集合,如果我们想要使用这个泛型列表,可以在使用时指定具体数据类型。
泛型的表示为 “T” 如:List
System.Collections.Generic 命名空间包含定义泛型集合的接口和类,泛型集合允许用户创建强类型集合,它能提供比非泛型强类型集合更好的类型安全性和性能。
创建泛型类的过程为:从一个现有的具体类开始,逐一将每个类型更改为类型参数,直至达到通用化和可用性的最佳平衡
【3】:泛型的好处
1 : 使代码更加的简洁,清晰
从前面我们也提到了,泛型具备可重用性 , 减少我们代码量, 使我们的程序更容易开发和维护,举例 :
实例 1 : 在从数据库中获取数据库的时候,我们经常会返回一个DataTable类型,然后将其转换为List集合. 那么如果没有泛型的话,我会一般会这样来做.
C#
public List<TrainingUser>GetTrainingUser(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 List<TrainingUser> DataTableToList(DataTabledt) { List<TrainingUser> list = new List<TrainingUser>(); 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中,我们传入了一个DataTable的对象,然后在去循环遍历每一行的对象值从而去赋值给TrainingUser类型对象,这只是其中的一个方法,如果我们的类型还有 Training/User/Project等类型的话,我们是不是就要写很多如同DataTableToList这样的方法呢? 这就体现出了这样的方式,会造成代码的冗余以及维护不便问题,那么我们使用泛型来解决
实例 2 : 使用泛型使上面的代码更见的清晰,简洁
C#
public static List<T> ToList1<T>(DataTable dt) whereT : class, new() { var prlist =new List<PropertyInfo>(); Type type = typeof(T); Array.ForEach( type.GetProperties(), p => { if(dt.Columns.IndexOf(p.Name) !=-1) { prlist.Add(p); } }); var oblist = new List<T>(); // 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; }
在上面的这个方法中,我们定义了一个泛型方法,内部实现中是使用了反射的原理,将DataTable转换为了List(反射后续随笔中总结,此处只关注泛型部分即可),我们定义了一个静态的返回值为List
C#
public List<TrainingUser>GetTrainingIdByUserId(string userId) { List<TrainingUser> trainingUserList = DataTableHelper.ToList1<TrainingUser>( 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
这样即便我们后续还有Training/User/Project等其他的类型,我们都可以直接使用DataTableHelper.ToList1
2 : 提升程序的性能
泛型与非泛型相比较而言,性能要好一些,这是为什么? 首先,泛型的类型是由调用者(接收者),去直接赋值的(类型安全的), 那么就不会存在类型转换的问题,其次, 泛型减少了装箱和拆箱的过程.
实例 3 : 对于值类型泛型与非泛型的性能比较
C#
private static void ListTest() { List<int>list = new List<int>(); 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执行值类型方法历时 : "+ sw.Elapsed.ToString()); sw.Stop(); Stopwatch sw1 = new Stopwatch(); sw1.Start(); ArrListTest(); Console.WriteLine(" 使用非泛型ArrayList执行值类型方法历时 : "+ sw1.Elapsed.ToString()); sw1.Stop(); Console.ReadLine();
通过循环 100 来比较,结果为 :
我们可以看到非泛型的时间要比泛型的时间多出0.0000523秒,泛型比非泛型的时间要多出一些, 那么我们将数值改动一下改为循环 1000次.得出结果为 :
泛型比非泛型执行的时间要短0.0000405秒
我们将时间在改动一下,改为 100000呢?结果为 :
这次差距为 0.0054621 并且随着执行次数的增长,非泛型相比泛型的时间会逐步的增加,
通过反编译我们也能看出 :
泛型:
非泛型
從編譯中我們也能看出泛型方法中,接收的為Int32,非泛型為Object,其次泛型不會進行裝箱和拆箱操作,非泛型每次執行都要進行裝箱和拆箱操作.
3 : 類型安全
在實例1 , 2 ,3 中我們都有備註說明,泛型的發送著必須要和接收者進行一致,否則會報異常,例如:
實例4 :
將一個泛型演算法應用於一個具體的類型時,編譯器和CLR能理解開發人員的意圖,並保證只有與指定資料類型相容的物件才能隨同演算法使用,若試圖使用不相容類型的一個對象,會造成編譯時錯誤,或運行時拋出異常
更多.NET泛型解析相關文章請關注PHP中文網!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

Go中的泛型函數解決了可變參數類型的問題:泛型函數允許使用類型參數,在運行時指定。這使得編寫可以處理不同類型參數的函數成為可能。例如,Max函數是一個泛型函數,它接受兩個可比較參數並傳回較大值。透過使用泛型函數,我們可以編寫更靈活通用的程式碼,可處理不同類型的參數。

泛型在Go中的應用場景:集合運算:建立適用於任何類型的集合運算,例如篩選。資料結構:編寫通用的資料結構,如佇列,堆疊和映射,可儲存和操作各種類型的資料。演算法:編寫通用的演算法,如排序,搜尋和歸約,可處理不同類型的資料。

Java函數泛型允許設定上限和下限。上限(extends)指定函數接受或傳回的資料類型必須是指定類型的子類型,例如。下限(super)指定函數接受或傳回的資料類型必須是指定類型的超類型,例如。泛型使用可提高程式碼的可重複使用性和安全性。

泛型對Go函數簽章和參數的影響包括:型別參數:函數簽章可包含型別參數,指定函數可使用的型別。類型約束:類型參數可具有約束,指定其必須滿足的條件。參數類型推斷:編譯器可推斷未指定型別參數的型別。指定類型:可明確指定參數類型以呼叫泛型函數。這提高了程式碼的可重複使用性和靈活性,允許編寫可與多種類型一起使用的函數和類型。

泛型在Android開發中的應用加強了程式碼的可重複使用性、安全性和靈活性。其語法包括宣告一個類型變數T,可用於操作類型參數化的資料。泛型實戰案例包括自訂資料適配器,允許適配器適應任何類型的自訂資料物件。 Android還提供了泛型清單類別(如ArrayList)和泛型方法,允許操作不同類型的參數。使用泛型的好處包括程式碼可重複使用性、安全性和靈活性,但需要注意指定正確的界限並適度使用,以確保程式碼的可讀性。

答案:Golang泛型是提高程式碼可重複使用性、靈活性、型別安全性和可擴充性的強大工具。詳細描述:優勢:程式碼可重複使用性:通用演算法和資料結構靈活性:執行階段建立特定類型實例類型安全性:編譯時類型檢查可擴展性:易於擴展和自訂用途:通用函數:排序、比較等通用資料結構:列表、映射、堆疊等類型別名:簡化類型聲明約束泛型:確保類型安全性

Java中枚舉型別與泛型的結合:宣告帶有泛型的枚舉時需加上尖括號,T為型別參數。建立泛型類別時,同樣需新增尖括號,T為可儲存任何類型的類型參數。此結合可提高程式碼靈活性、類型安全性,並簡化程式碼。
