詳細(xì)介紹C# 泛型
在C#開(kāi)發(fā)中,必不可少的要用到泛型。泛型是.NET2.0版本就有的,它廣泛應(yīng)用于C#框架中容器的使用中。下面我們來(lái)詳細(xì)介紹一下。
一、泛型的主要優(yōu)勢(shì)
1.性能更高。
2.類(lèi)型更安全。
3.代碼更多的重用和擴(kuò)展性。
二、泛型的基本使用
泛型的一個(gè)主要優(yōu)點(diǎn)是性能,我們來(lái)看下面的例子:
static void Main(string[] args) { //不是泛型的集合類(lèi) ArrayList list = new ArrayList(); //添加一個(gè)值類(lèi)型 裝箱操作 list.Add(12); //去除第一個(gè)元素12 拆箱操作 int num = (int)list[0]; Console.WriteLine(num); Console.WriteLine("執(zhí)行結(jié)束"); Console.ReadKey(); }
元數(shù)據(jù)中ArrayList類(lèi)的Add方法
// // 摘要: // 將對(duì)象添加到 System.Collections.ArrayList 的結(jié)尾處。 // // 參數(shù): // value: // 要添加到 System.Collections.ArrayList 末尾的 System.Object。該值可以為 null。 // // 返回結(jié)果: // value 已添加的 System.Collections.ArrayList 索引。 // // 異常: // T:System.NotSupportedException: // The System.Collections.ArrayList is read-only.-or- The System.Collections.ArrayList // has a fixed size. public virtual int Add(object value);
相信大家都知道,裝箱拆箱是比較損耗性能的,在執(zhí)行add方法是, 把值類(lèi)型轉(zhuǎn)換成引用類(lèi)型(裝箱),取出來(lái)時(shí)在去拆箱,那怎么樣才能避免這種情況發(fā)生呢?
再來(lái)看下面代碼:
//泛型集合類(lèi) List<int> list = new List<int>(); list.Add(12); int num = list[0]; Console.WriteLine(num); Console.WriteLine("執(zhí)行結(jié)束"); Console.ReadKey();
這個(gè)時(shí)候,代碼并沒(méi)有發(fā)生裝箱拆箱操作。
元數(shù)據(jù)中List類(lèi)的Add方法
// // 摘要: // 將對(duì)象添加到 System.Collections.Generic.List`1 的結(jié)尾處。 // // 參數(shù): // item: // 要添加到 System.Collections.Generic.List`1 末尾的對(duì)象。對(duì)于引用類(lèi)型,該值可以為 null。 public void Add(T item);
代碼寫(xiě)到這里時(shí),我們只是創(chuàng)建了一個(gè)List泛型集合,你可能還感覺(jué)不到泛型優(yōu)勢(shì)到底在哪里,你也可能不知道泛型到底是怎么用的。好,下面我們寫(xiě)個(gè)測(cè)試還有自己實(shí)際應(yīng)用的例子。
測(cè)試arraylist和list集合的性能
static void Main(string[] args) { //不是泛型的集合類(lèi) ArrayList arylist = new ArrayList(); //添加一個(gè)值類(lèi)型 裝箱操作 //泛型集合類(lèi) List<int> list = new List<int>(); //計(jì)時(shí)類(lèi) Stopwatch watch = new Stopwatch(); watch.Start();//開(kāi)始計(jì)時(shí) for (int i = 0; i < 10000000; i++) { arylist.Add(i); } watch.Stop(); Console.WriteLine("Arraylist用時(shí):"+watch.ElapsedMilliseconds); Stopwatch watch1 = new Stopwatch(); watch1.Start();//開(kāi)始計(jì)時(shí) for (int i = 0; i < 10000000; i++) { list.Add(i); } watch1.Stop(); Console.WriteLine("list用時(shí):" + watch1.ElapsedMilliseconds); Console.WriteLine("執(zhí)行結(jié)束"); Console.ReadKey(); }
執(zhí)行結(jié)果:
以上的例子中,可以看出在計(jì)時(shí)的過(guò)程中代碼寫(xiě)的重復(fù)了, 怎么解決這個(gè)問(wèn)題呢, 泛型要排上用場(chǎng)了。
我們想一下,相同的操作(都是循環(huán)添加元素,計(jì)算用時(shí))用同一個(gè)方法實(shí)現(xiàn)不就ok了,只不過(guò)這個(gè)方法是泛型的(可以接受ArrayList,也可以接受List),下面我們來(lái)寫(xiě)一下這個(gè)方法?! ?/p>
//我們用T來(lái)代表泛型 public static long GetTime<T>(T t) { Stopwatch watch = new Stopwatch(); watch.Start();//開(kāi)始計(jì)時(shí) for (int i = 0; i < 10000000; i++) { t.Add(i); } watch.Stop(); return watch.ElapsedMilliseconds; }
但是問(wèn)題來(lái)了, 這里并沒(méi)有Add方法讓我們使用啊。 我們的思路是把這個(gè)T在調(diào)用時(shí)可以當(dāng)作ArrayList類(lèi)型, 也可以當(dāng)作List類(lèi)型,這里就設(shè)計(jì)到了泛型約束。
三、泛型約束
如果使用泛型時(shí), 想要調(diào)用這個(gè)泛型類(lèi)型中的方法, 那么就需要添加約束。泛型約束主要有以下幾種:
約束 | 說(shuō)明 |
where T:struct | 對(duì)于結(jié)構(gòu)的約束, T必須是值類(lèi)型 |
where T:class | T必須是引用類(lèi)型 |
where T:ITest | T必須實(shí)現(xiàn)了ITest接口 |
where T:Test | T必須繼承基類(lèi)Test |
where T:new() | T必須有默認(rèn)構(gòu)造函數(shù) |
where T:T2 | T派生自泛型類(lèi)型T2,也稱(chēng)為裸類(lèi)型約束 |
我們接著上個(gè)泛型方法來(lái)修改,ArrayList和List都實(shí)現(xiàn)了接口IList , 這個(gè)時(shí)候我們加上這個(gè)接口約束;
//我們用T來(lái)代表泛型 public static long GetTime<T>(T t)where T:IList { Stopwatch watch = new Stopwatch(); watch.Start();//開(kāi)始計(jì)時(shí) for (int i = 0; i < 10000000; i++) { t.Add(i); } watch.Stop(); return watch.ElapsedMilliseconds; }
調(diào)用結(jié)果:
代碼寫(xiě)到這里時(shí),相信你已經(jīng)對(duì)泛型有所了解了,但是真要應(yīng)用到自己以后的邏輯編程中時(shí),一定要善于總結(jié):相同類(lèi)型的相同方法,或者業(yè)務(wù)邏輯相同,只有某個(gè)判斷不同時(shí),可以用上泛型,不僅高效還代碼量小。
四、應(yīng)用場(chǎng)景示范
在我們的項(xiàng)目開(kāi)發(fā)中,數(shù)據(jù)庫(kù)的增刪改查肯定是少不了的, 在這里我們用泛型來(lái)定義增刪改查的泛型類(lèi)。 之后建立一個(gè)用戶(hù)表(實(shí)際應(yīng)用中對(duì)應(yīng)數(shù)據(jù)庫(kù)中表結(jié)構(gòu)),數(shù)據(jù)庫(kù)中每一個(gè)表都可以用泛型類(lèi)中定義的方法, 不需要每一個(gè)都寫(xiě)增刪改查操作,也是面向?qū)ο缶幊痰囊环N思想:
public class BaseDal<T>where T:class ,new () { //以下是增刪查改示范 public void Query(int id) { Console.WriteLine(typeof(T).Name+"查詢(xún)方法,id="+id); } public void Update(T t) { Console.WriteLine(typeof(T).Name+"更新方法"); } public void Delete(T t) { Console.WriteLine(typeof(T).Name + "刪除方法"); } public void Add(T t) { Console.WriteLine(typeof(T).Name + "添加方法"); } }
public class User { public int Id { get; set; } public string Name { get; set; } }
調(diào)用示范
BaseDal<User> dal = new BaseDal<User>(); var user = new User() { Id = 0, Name = "用戶(hù)1" }; dal.Add(user); dal.Query(0); user.Name = "用戶(hù)11"; dal.Update(user); dal.Delete(user); Console.ReadKey();
五、泛型的協(xié)變和抗變
協(xié)變和抗變主要是對(duì)參數(shù)和返回值的類(lèi)型進(jìn)行轉(zhuǎn)換,在.NET4之后可以通過(guò)協(xié)變和抗變?yōu)榉盒徒涌诨蜻@泛型委托添加這個(gè)擴(kuò)展。
現(xiàn)在我們寫(xiě)倆個(gè)類(lèi)Shape(形狀)、Rectangle(矩形類(lèi)),Rectangle派生自Shape,寫(xiě)一個(gè)方法public static Rectangle GetRec() ;這個(gè)時(shí)候你會(huì)發(fā)現(xiàn), 方法的泛型類(lèi)型是抗變的, 就是我返回一個(gè)類(lèi)型為Rectangle但是我可以用Shape來(lái)接收, 但泛型在NET4.0之前不支持這個(gè)方式, 泛型在NET4.0之后提供了支持泛型接口和泛型委托的協(xié)變和抗變。
普通方法抗變代碼說(shuō)明
//形狀 public class Shape { public double Width { get; set; } public double Height { get; set; } public override string ToString() { return string.Format("width:{0},height:{1}",Width,Height); } } //矩形 public class Rectangle : Shape { } ///-----------------------------------方法與調(diào)用 public static Rectangle GetRec() { return new Rectangle(); } Shape s = GetRec(); Console.ReadKey();
1、泛型接口的協(xié)變
泛型接口在類(lèi)型T前加上out關(guān)鍵字,這個(gè)時(shí)候泛型接口就是協(xié)變的,也就意味著返回類(lèi)型只能是T。 直接看代碼:
//泛型接口的協(xié)變 public interface IIndex<out T> { T GetT(int index); int Count { get; } } public class RecCollection : IIndex<Rectangle> { private Rectangle[] data = new Rectangle[2] { new Rectangle() { Width=10,Height=20 }, new Rectangle() {Width=20,Height=30 } }; public int Count { get { return data.Count(); } } public Rectangle GetT(int index) { return data[index]; } }
//調(diào)用 Shape s1 = new RecCollection().GetT(1); Console.WriteLine(s1.ToString()); IIndex<Rectangle> rec = new RecCollection(); IIndex<Shape> shapes = rec; for (int i = 0; i < shapes.Count; i++) { Console.WriteLine(shapes.GetT(i)); } Console.ReadKey();
以上代碼可以看出, 我們把泛型接口的泛型定義為矩形(Rectangle), 但是在接受的時(shí)候可以用基類(lèi)(Shape) ,在這里實(shí)現(xiàn)了協(xié)變。
2.泛型接口的抗變
如果泛型類(lèi)型用in關(guān)鍵字標(biāo)注,那么這個(gè)泛型接口就是抗變的,這樣,接口只能把泛型類(lèi)型T用作其方法的輸入。
//泛型接口的抗變 public interface IDisplay<in T> { void Show(T item); } public class ShapeDisplay : IDisplay<Shape> { public void Show(Shape item) { Console.WriteLine(item); } } //-------------------------以下調(diào)用------ Rectangle recs = new Rectangle() { Width=100,Height=200}; IDisplay<Shape> shapeDisplay = new ShapeDisplay(); shapeDisplay.Show(recs); IDisplay<Rectangle> recDisplay = shapeDisplay; recDisplay.Show(recs); Console.ReadKey();
以上代碼可以看出泛型也是支持抗變和協(xié)變的。
六、總結(jié)
泛型是C#語(yǔ)言發(fā)展帶來(lái)的新方法,以上例子只是簡(jiǎn)單的運(yùn)用,希望大家能通過(guò)以上例子有所啟發(fā),能在項(xiàng)目中更好的使用泛型。以上還有泛型緩存沒(méi)有說(shuō)到,大家有興趣可以找下資料,今天就到這里吧, 沒(méi)有說(shuō)到的還有不好的地方, 歡迎大家指正!
以上就是詳細(xì)介紹C# 泛型的詳細(xì)內(nèi)容,更多關(guān)于C# 泛型的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
通過(guò)C#編寫(xiě)一個(gè)簡(jiǎn)易的Windows截屏增強(qiáng)工具
在使用?Windows?系統(tǒng)的截屏快捷鍵?PrintScreen?截屏?xí)r,如果需要把截屏保存到文件,需要先粘貼到畫(huà)圖工具然后另存為文件。所以本文用C#編寫(xiě)了一個(gè)簡(jiǎn)易的Windows截屏增強(qiáng)工具,需要的可以參考一下2022-05-05從Request.Url中獲取根網(wǎng)址的簡(jiǎn)單操作
這篇文章主要介紹了從Request.Url中獲取根網(wǎng)址的簡(jiǎn)單操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-01-01基于C#實(shí)現(xiàn)鼠標(biāo)設(shè)置功能
這篇文章主要為大家詳細(xì)介紹了如何利用C#實(shí)現(xiàn)鼠標(biāo)設(shè)置功能,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)C#有一定的幫助,感興趣的小伙伴可以跟隨小編一起了解一下2022-12-12C#實(shí)現(xiàn)簡(jiǎn)單播放mp3的方法
這篇文章主要介紹了C#實(shí)現(xiàn)簡(jiǎn)單播放mp3的方法,涉及C#播放多媒體文件的技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-03-03