寫在前面 今天忙裡偷閑在瀏覽外文的時候看到一篇講C 中泛型的使用的文章,因此加上本人的理解以及四級沒過的英語水平斗膽給大伙進行了翻譯,當然在翻譯的過程中發現了一些問題,因此也進行了糾正,當然,原文的地址我放在最下麵,如果你的英文水平比較好的話,可以直接直接閱讀全文。同時最近建了一個.NET Core ...
寫在前面
今天忙裡偷閑在瀏覽外文的時候看到一篇講C#中泛型的使用的文章,因此加上本人的理解以及四級沒過的英語水平斗膽給大伙進行了翻譯,當然在翻譯的過程中發現了一些問題,因此也進行了糾正,當然,原文的地址我放在最下麵,如果你的英文水平比較好的話,可以直接直接閱讀全文。同時最近建了一個.NET Core實戰項目交流群637326624,有興趣的朋友可以來相互交流。目前.NET Core實戰項目之CMS的教程也已經更新了6篇了,目前兩到三天更新一篇。
作者:依樂祝
原文地址:https://www.cnblogs.com/yilezhu/p/10029782.html
介紹
C#和.NET中的泛型程式具有強類型集合的許多優點,併為代碼提供更高質量和性能提升。泛型是C#語言和公共語言運行庫(CLR)中的一個新功能,它將類型參數的概念引入.NET Framework。類型參數使得設計某些類和方法成為可能,例如,通過使用泛型類型參數T,可以大大簡化類型之間的強制轉換或裝箱操作的過程(裝箱、拆箱問題)。說白了,泛型就是通過參數化類型來實現在同一份代碼上操作多種數據類型,利用“參數化類型”將類型抽象化,從而實現靈活的復用。每個集合的詳細規範可以在System.Collection.Generic名稱空間下找到。
裝箱和拆箱
.Net定義了兩種主要的數據類型來表示變數,也就是傳說中的值類型和引用類型。這是需要裝箱和拆箱的地方。裝箱是一種通過將變數存儲到System.Object中來顯式地將值類型轉換為引用類型的機制。當您裝入值時,CLR會將新對象分配到堆中,並將值類型的值複製到該實例中。例如,您創建了一個int類型的變數:
int a = 20;
object b = a; //裝箱
相反的操作是拆箱,它是將引用類型轉換回值類型的過程。此過程驗證接收數據類型是否與裝箱類型一致;
int c = (int)b; // 拆箱
C#編譯器可以看到從int到object的賦值,反之亦然。當編譯該程式並通過IL解析器檢查IL生成的代碼時,您會註意到當b被賦值為a時,程式通過在IL中自動插入一個box指令來響應,當c被賦值為b時如下;
代碼載入常量20並將其存儲在本地插槽中;它將值20載入到堆棧中並將其裝箱。最後,它將被裝箱的20返回到堆棧上,並將其拆箱為int類型
這個過程.NET CLR執行了一系列操作,例如,首先在托管堆中分配一個對象,然後在裝箱中將值轉換為記憶體位置,併在拆箱期間將值存儲在堆上並且必須轉回到堆棧。因此,從性能的角度來看,裝箱和拆箱過程在泛型中具有非常重要的意義,因為這個過程如果不使用泛型的話會耗費更多地資源。
泛型類
可以通過在類名後面加上
public class TestClass<T> { }
System.Collection.Generic命名空間下還定義了許多實現了這些關鍵字介面的類型。下表列出了此命名空間的核心類類型。
泛型類 | 描述 |
---|---|
Collection |
泛型集合的基類,可以比較兩個泛型對象是否相等 |
Dictionary<TKey, TValue> | 鍵值對的泛型集合 |
List |
可動態調整列表項的大小 |
Queue |
先進先出(FIFO)列表的泛型實現 |
Stack |
後進先出(LIFO)列表的泛型實現 |
簡單的泛型類示例
以下示例顯示了一個簡單的泛型類型的操作。TestClass
using System;
using System.Collections.Generic;
namespace GenericApp
{
public class TestClass<T>
{
// 定義一個長度為5的泛型類型的數組
T[] obj = new T[5];
int count = 0;
// 向反省類型添加數據
public void Add(T item)
{
//checking length
if (count + 1 < 6)
{
obj[count] = item;
}
count++;
}
//foreach語句迭代索引
public T this[int index]
{
get { return obj[index]; }
set { obj[index] = value; }
}
}
class Program
{
static void Main(string[] args)
{
//用整形來實例化泛型類
TestClass<int> intObj = new TestClass<int>();
//向集合中添加int數據
intObj.Add(1);
intObj.Add(2);
intObj.Add(3); //沒有裝箱
intObj.Add(4);
intObj.Add(5);
//遍歷顯示數據
for (int i = 0; i < 5; i++)
{
Console.WriteLine(intObj[i]); //沒有拆箱
}
Console.ReadKey();
}
}
}
在構建並運行該程式之後,程式的輸出如下所示;
泛型的主要特性
泛型類型的一些重要特征使它們相比傳統的非泛型類型具有如下的顯著特征:
- 類型安全
- 性能
- 二進位代碼復用
類型安全
泛型最重要的特征之一是類型安全性。對於非泛型ArrayList類,如果使用對象類型,則可以向集合中添加任何類型,這些類型有時會導致嚴重的問題。下麵的示例顯示向ArrayList類型的集合添加一個整數、字元串和對象;
ArrayList obj = new ArrayList();
obj.Add(50);
obj.Add("Dog");
obj.Add(new TestClass());
現在,如果使用整數對象來使用foreach語句進行遍歷的話,當編譯器接受到代碼,但是因為集合中的所有元素都不是整數,所以會導致運行時異常;
foreach(int i in obj)
{
Console.WriteLine(i);
}
編程的經驗法則是應該儘早檢測到錯誤。對於泛型類Test
Test<int> obj = new Test<int>();
obj.Add(50);
obj.Add("Dog"); //編譯錯誤
obj.Add(new TestClass()); //編譯錯誤
性能
在下麵的示例中,ArrayList類存儲對象,並且定義了Add()方法來存儲一些整型參數。因此,整數類型被裝箱。當使用foreach語句讀取ArrayList中的值時,將發生拆箱。
ArrayList obj = new ArrayList();
obj.Add(50); //裝箱- 值類型轉換成引用類型
int x= (int)obj[0]; //拆箱
foreach(int i in obj)
{
Console.WriteLine(i); // 拆箱
}
註意:泛型比其他集合(如ArrayList)更快。
代替使用對象類型,TestClass
TestClass<int> obj = new TestClass<int>();
obj.Add(50); //沒有裝箱
int x= obj[0]; // 沒有拆箱
foreach(int i in obj)
{
Console.WriteLine(i); //沒有拆箱
}
二進位代碼復用
泛型類型提供了一種源代碼保護機制。泛型類可以定義一次,並且可以使用許多不同類型來進行實例化。泛型可以在一種CLR支持的語言中定義,並可以被另一種.NET語言使用。以下TestClass
TestClass<int> obj = new TestClass<int>();
obj.Add(50);
TestClass<string> obj1 = new TestClass<string>();
Obj1.Add("hello");
通用方法
雖然大多數開發人員通常會使用基類庫中的現有泛型類型,但也有可能會構建自己的泛型成員和自定義的泛型類型。
本示例的目的是構建一個交換方法,該方法可以使用單個類型參數對任何可能的數據類型(基於值或基於引用)進行操作。由於交換演算法的性質,傳入的參數將作為使用ref關鍵字修飾的引用類型來進行發送。
using System;
using System.Collections.Generic;
namespace GenericApp
{
class Program
{
//泛型方法
static void Swap<T>(ref T a, ref T b)
{
T temp;
temp = a;
a = b;
b = temp;
}
static void Main(string[] args)
{
//交換兩個整形數據
int a = 40, b = 60;
Console.WriteLine("Before swap: {0}, {1}", a, b);
Swap<int>(ref a, ref b);
Console.WriteLine("After swap: {0}, {1}", a, b);
Console.ReadLine();
}
}
}
編譯此泛型方法實現的程式後,輸出如下所示;
字典
字典也被稱為映射或散列表。它表示允許您基於關鍵字來訪問元素的數據結構。字典的一個重要特征是更快的查找; 您可以添加或刪除選項而不會產生性能開銷。
.Net提供了幾個字典類,例如Dictionary <TKey,TValue>。類型參數TKey和TValue分別表示關鍵字的類型和它可以存儲的值。
簡單的字典示例
以下示例演示使用泛型的簡單字典集合。在此程式中,將創建一個Dictionary類型對象,該對象接受int作為鍵,字元串作為值。然後我們將一些字元串值添加到字典集合中,最後顯示字典集合元素。
using System;
using System.Collections.Generic;
namespace GenericApp
{
public class Program
{
static void Main(string[] args)
{
//定義一個字典集合
Dictionary<int,string> dObj = new Dictionary<int,string>(5);
//向字典中添加類型
dObj.Add(1,1,"Tom");
dObj.Add(2,"John");
dObj.Add(3, "Maria");
dObj.Add(4, "Max");
dObj.Add(5, "Ram");
//輸出數據
for (int i = 1; i <= dObj.Count;i++)
{
Console.WriteLine(dObj[i]);
}
Console.ReadKey();
}
}
}
以下示例通過定義附加類emp來描述一些更複雜的問題,其中我們覆蓋ToString()方法以顯示特定員工的名稱和薪水。稍後在Main()方法中,創建一個新的Dictionary <TKey,TValue)的實例,其中鍵的類型為string,值為emp類型。構造函數分配2個元素的容量。emp對象和作為鍵的字元串值被添加到字典集合中。最後,使用foreach語句迭代集合元素並顯示在屏幕上。
using System;
using System.Text;
using System.Collections.Generic;
namespace GenericApp
{
public class emp
{
private string name;
private int salary;
public emp(string name,int salary)
{
this.name = name;
this.salary = salary;
}
public override string ToString()
{
StringBuilder sb = new StringBuilder(200);
sb.AppendFormat("{0},{1}",name,salary);
return sb.ToString();
}
}
public class Program
{
static void Main(string[] args)
{
//定義一個字典集合
Dictionary<string, emp> dObj = new Dictionary<string, emp>(2);
//向字典中添加元素
emp tom = new emp("tom", 2000);
dObj.Add("tom",tom); // 鍵,值
emp john = new emp("john", 4000);
dObj.Add("john",john);
//print data
foreach(Object str in dObj.Values)
{
Console.WriteLine(str);
}
Console.ReadKey();
}
}
}
隊列
隊列是一種特殊類型的容器,可確保以FIFO(先進先出)方式訪問元素。隊列集合最適合實現消息傳遞的組件。我們可以使用以下語法定義Queue集合對象:
Queue qObj = new Queue();
Queue集合的屬性,方法和其他規則定義都位於Sysyem.Collection命名空間下。下表定義了關鍵成員;
System.Collection.Queue成員 | 定義 |
---|---|
Enqueue() | 將對象添加到隊列的末尾。 |
Dequeue() | 從隊列的開頭刪除對象。 |
Peek() | 返回隊列開頭的對象而不刪除它。 |
下麵演示了一個基本的隊列類型的集合,將一些字元串類型值添加到集合中,最後使用while語句來顯示整個集合中的數據 。
using System;
using System.Collections;
namespace GenericApp
{
class Program
{
static void Main(string[] args)
{
//定義一個隊列
Queue qObj = new Queue();
//向隊列中添加字元串數據
qObj.Enqueue("Tom");
qObj.Enqueue("Harry");
qObj.Enqueue("Maria");
qObj.Enqueue("john");
//顯示隊列中的數據
while(qObj.Count !=0 )
{
Console.WriteLine(qObj.Dequeue());
}
Console.ReadKey();
}
}
}
堆棧
Stack集合是LIFO的抽象(後進先出)。我們可以使用以下語法定義Stack集合對象:
Stack qObj = new Stack();
下表說明瞭堆棧的關鍵成員;
System.Collection.Stack成員 | 定義 |
---|---|
Contains() | 如果在集合中找到特定元素,則返回true。 |
Clear() | 刪除集合的所有元素。 |
Peek() | 預覽堆棧中的最新元素。 |
Push() | 它將元素推入堆棧。 |
Pop() | 返回並刪除堆棧的頂部元素。 |
以下演示了堆棧集合。首先,將數組類型對象引用到堆棧集合中。然後使用Pop()方法從堆棧中刪除集合中元素的值並顯示在屏幕上。
using System;
using System.Collections;
namespace GenericApp
{
class Program
{
static void Main(string[] args)
{
int[] iArray = new int[] {1,2,3,4,5,6,7,8,9,10 };
//定義一個堆棧
Stack sObj = new Stack(iArray);
Console.WriteLine("Total items="+sObj.Count);
//顯示集合數據
for (int i = 0; i < sObj.Count;++i )
{
Console.WriteLine(sObj.Pop());
}
Console.ReadKey();
}
}
}
在使用泛型實現的另一個示例中,使用Push()方法將5個項添加到堆棧中。然後使用迴圈迭代輸出堆棧中的數據。堆棧的枚舉器不會刪除數據; 它只是以LIFO方式返回每個項目,如下所示:
using System;
using System.Collections;
namespace GenericApp
{
class Program
{
static void Main(string[] args)
{
//定義一個堆棧
Stack sObj = new Stack();
//向集合添加數據
for (int i = 0; i < 5; ++i)
{
sObj.Push(i+1);
}
Console.WriteLine("Total items=" + sObj.Count);
//列印數據
foreach (int i in sObj)
{
Console.WriteLine(i);
}
Console.ReadKey();
}
}
}
總結
今天忙裡偷閑,在瀏覽外文的時候看到一篇講泛型的文章,因此就加上自己的理解進行了相關翻譯,也加深了自己對泛型的理解!如果英文比較好的話可以直接訪問https://www.c-sharpcorner.com/UploadFile/84c85b/using-generics-with-C-Sharp/ 自行查看!當然,我在翻譯的過程中也發現了文中的一些錯誤,所以進行了更正!同時最近建了一個.NET Core實戰項目交流群637326624,有興趣的朋友可以來相互交流。目前.NET Core實戰項目之CMS的教程也已經更新了6篇了,目前兩到三天更新一篇。最後感謝大家的閱讀。