這篇文章給大家分享的是有關C#中泛型指的是什么的內容。小編覺得挺實用的,因此分享給大家做個參考。一起跟隨小編過來看看吧。
在C#開發中,必不可少的要用到泛型。泛型是.NET2.0版本就有的,它廣泛應用于C#框架中容器的使用中。下面我們來詳細介紹一下。
一、泛型的主要優勢
1.性能更高。
2.類型更安全。
3.代碼更多的重用和擴展性。
二、泛型的基本使用
泛型的一個主要優點是性能,我們來看下面的例子:
static void Main(string[] args)
{
//不是泛型的集合類
ArrayList list = new ArrayList();
//添加一個值類型 裝箱操作
list.Add(12);
//去除第一個元素12 拆箱操作
int num = (int)list[0];
Console.WriteLine(num);
Console.WriteLine("執行結束");
Console.ReadKey();
}元數據中ArrayList類的Add方法
// // 摘要: // 將對象添加到 System.Collections.ArrayList 的結尾處。 // // 參數: // value: // 要添加到 System.Collections.ArrayList 末尾的 System.Object。該值可以為 null。 // // 返回結果: // 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);
相信大家都知道,裝箱拆箱是比較損耗性能的,在執行add方法是, 把值類型轉換成引用類型(裝箱),取出來時在去拆箱,那怎么樣才能避免這種情況發生呢?
再來看下面代碼:
//泛型集合類
List<int> list = new List<int>();
list.Add(12);
int num = list[0];
Console.WriteLine(num);
Console.WriteLine("執行結束");
Console.ReadKey();這個時候,代碼并沒有發生裝箱拆箱操作。
元數據中List類的Add方法
// // 摘要: // 將對象添加到 System.Collections.Generic.List`1 的結尾處。 // // 參數: // item: // 要添加到 System.Collections.Generic.List`1 末尾的對象。對于引用類型,該值可以為 null。 public void Add(T item);
代碼寫到這里時,我們只是創建了一個List泛型集合,你可能還感覺不到泛型優勢到底在哪里,你也可能不知道泛型到底是怎么用的。好,下面我們寫個測試還有自己實際應用的例子。
測試arraylist和list集合的性能
static void Main(string[] args)
{
//不是泛型的集合類
ArrayList arylist = new ArrayList();
//添加一個值類型 裝箱操作
//泛型集合類
List<int> list = new List<int>();
//計時類
Stopwatch watch = new Stopwatch();
watch.Start();//開始計時
for (int i = 0; i < 10000000; i++)
{
arylist.Add(i);
}
watch.Stop();
Console.WriteLine("Arraylist用時:"+watch.ElapsedMilliseconds);
Stopwatch watch2 = new Stopwatch();
watch2.Start();//開始計時
for (int i = 0; i < 10000000; i++)
{
list.Add(i);
}
watch2.Stop();
Console.WriteLine("list用時:" + watch2.ElapsedMilliseconds);
Console.WriteLine("執行結束");
Console.ReadKey();
}執行結果:

以上的例子中,可以看出在計時的過程中代碼寫的重復了, 怎么解決這個問題呢, 泛型要排上用場了。
我們想一下,相同的操作(都是循環添加元素,計算用時)用同一個方法實現不就ok了,只不過這個方法是泛型的(可以接受ArrayList,也可以接受List),下面我們來寫一下這個方法?! ?/p>
//我們用T來代表泛型
public static long GetTime<T>(T t)
{
Stopwatch watch = new Stopwatch();
watch.Start();//開始計時
for (int i = 0; i < 10000000; i++)
{
t.Add(i);
}
watch.Stop();
return watch.ElapsedMilliseconds;
}但是問題來了, 這里并沒有Add方法讓我們使用啊。 我們的思路是把這個T在調用時可以當作ArrayList類型, 也可以當作List類型,這里就設計到了泛型約束。
三、泛型約束
如果使用泛型時, 想要調用這個泛型類型中的方法, 那么就需要添加約束。泛型約束主要有以下幾種:
| 約束 | 說明 |
| where T:struct | 對于結構的約束, T必須是值類型 |
| where T:class | T必須是引用類型 |
| where T:ITest | T必須實現了ITest接口 |
| where T:Test | T必須繼承基類Test |
| where T:new() | T必須有默認構造函數 |
| where T:T2 | T派生自泛型類型T2,也稱為裸類型約束 |
我們接著上個泛型方法來修改,ArrayList和List都實現了接口IList , 這個時候我們加上這個接口約束;
//我們用T來代表泛型
public static long GetTime<T>(T t)where T:IList
{
Stopwatch watch = new Stopwatch();
watch.Start();//開始計時
for (int i = 0; i < 10000000; i++)
{
t.Add(i);
}
watch.Stop();
return watch.ElapsedMilliseconds;
}調用結果:

代碼寫到這里時,相信你已經對泛型有所了解了,但是真要應用到自己以后的邏輯編程中時,一定要善于總結:相同類型的相同方法,或者業務邏輯相同,只有某個判斷不同時,可以用上泛型,不僅高效還代碼量小。
四、應用場景示范
在我們的項目開發中,數據庫的增刪改查肯定是少不了的, 在這里我們用泛型來定義增刪改查的泛型類。 之后建立一個用戶表(實際應用中對應數據庫中表結構),數據庫中每一個表都可以用泛型類中定義的方法, 不需要每一個都寫增刪改查操作,也是面向對象編程的一種思想:
public class BaseDal<T>where T:class ,new ()
{
//以下是增刪查改示范
public void Query(int id) {
Console.WriteLine(typeof(T).Name+"查詢方法,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; }
}調用示范
BaseDal<User> dal = new BaseDal<User>();
var user = new User()
{
Id = 0,
Name = "用戶1"
};
dal.Add(user);
dal.Query(0);
user.Name = "用戶11";
dal.Update(user);
dal.Delete(user);
Console.ReadKey();五、泛型的協變和抗變
協變和抗變主要是對參數和返回值的類型進行轉換,在.NET4之后可以通過協變和抗變為泛型接口或這泛型委托添加這個擴展。
現在我們寫倆個類Shape(形狀)、Rectangle(矩形類),Rectangle派生自Shape,寫一個方法public static Rectangle GetRec() ;這個時候你會發現, 方法的泛型類型是抗變的, 就是我返回一個類型為Rectangle但是我可以用Shape來接收, 但泛型在NET4.0之前不支持這個方式, 泛型在NET4.0之后提供了支持泛型接口和泛型委托的協變和抗變。
普通方法抗變代碼說明
//形狀
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 {
}
///-----------------------------------方法與調用
public static Rectangle GetRec() {
return new Rectangle();
}
Shape s = GetRec();
Console.ReadKey();1、泛型接口的協變
泛型接口在類型T前加上out關鍵字,這個時候泛型接口就是協變的,也就意味著返回類型只能是T。 直接看代碼:
//泛型接口的協變
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];
}
}//調用
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), 但是在接受的時候可以用基類(Shape) ,在這里實現了協變。
2.泛型接口的抗變
如果泛型類型用in關鍵字標注,那么這個泛型接口就是抗變的,這樣,接口只能把泛型類型T用作其方法的輸入。
//泛型接口的抗變
public interface IDisplay<in T>
{
void Show(T item);
}
public class ShapeDisplay : IDisplay<Shape>
{
public void Show(Shape item)
{
Console.WriteLine(item);
}
}
//-------------------------以下調用------
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();以上可以看出泛型也是支持抗變和協變的。
感謝各位的閱讀!關于C#中泛型指的是什么就分享到這里了,希望以上內容可以對大家有一定的幫助,讓大家可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。