[譯]聊聊C#中的泛型的使用(新手勿入)


寫在前面

今天忙里偷閑在瀏覽外文的時候看到一篇講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名稱空間下找到。

通用類C#

裝箱和拆箱

.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時如下;

IL-opcode.jpg

代碼加載常量20並將其存儲在本地插槽中;它將值20加載到堆棧中並將其裝箱。最后,它將被裝箱的20返回到堆棧上,並將其拆箱為int類型

這個過程.NET CLR執行了一系列操作,例如,首先在托管堆中分配一個對象,然后在裝箱中將值轉換為內存位置,並在拆箱期間將值存儲在堆上並且必須轉回到堆棧。因此,從性能的角度來看,裝箱和拆箱過程在泛型中具有非常重要的意義,因為這個過程如果不使用泛型的話會耗費更多地資源。

泛型類

可以通過在類名后面加上 符號來定義泛型類。這里沒有強制必須將“T”字放在泛型的定義中。您可以在TestClass <>類聲明中使用任何單詞。

public class TestClass<T> { }  

System.Collection.Generic命名空間下還定義了許多實現了這些關鍵字接口的類型。下表列出了此命名空間的核心類類型。

泛型類 描述
Collection 泛型集合的基類,可以比較兩個泛型對象是否相等
Dictionary<TKey, TValue> 鍵值對的泛型集合
List 可動態調整列表項的大小
Queue 先進先出(FIFO)列表的泛型實現
Stack 后進先出(LIFO)列表的泛型實現

簡單的泛型類示例

以下示例顯示了一個簡單的泛型類型的操作。TestClass 定義一個長度為5的泛型類型數組。Add()方法負責將任何類型的對象添加到集合中,而Indexer屬性是循環語句迭代的實現。最后在主類中,我們使用整形類型來實例化TestClass 類,並使用Add()方法將一些整數類型數據添加到集合中。

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();    
        }  
    }  
}  

在構建並運行該程序之后,程序的輸出如下所示;

簡單仿制Example.jpg

泛型的主要特性

泛型類型的一些重要特征使它們相比傳統的非泛型類型具有如下的顯著特征:

  • 類型安全
  • 性能
  • 二進制代碼復用

類型安全

泛型最重要的特征之一是類型安全性。對於非泛型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 ,泛型類型T定義允許哪些類型。通過使用Test 的定義,只能向集合添加整型類型的數據。這時候當Add()方法具有以下無效參數的時候編譯器將不編譯代碼;

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 類的泛型類型被定義為int,因此在從編譯器動態生成的類中將使用int類型。所以將不會發生裝箱和拆箱,如下所示;

TestClass<int> obj = new TestClass<int>();  
obj.Add(50);    //沒有裝箱 
int x= obj[0]; // 沒有拆箱
foreach(int i in obj)  
{  
   Console.WriteLine(i);   //沒有拆箱 
}  

二進制代碼復用

泛型類型提供了一種源代碼保護機制。泛型類可以定義一次,並且可以使用許多不同類型來進行實例化。泛型可以在一種CLR支持的語言中定義,並可以被另一種.NET語言使用。以下TestClass 使用int和string類型進行實例化:

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();  
        }  
    }  
}  

編譯此泛型方法實現的程序后,輸出如下所示;

通用-Methods.jpg

字典

字典也被稱為映射或散列表。它表示允許您基於關鍵字來訪問元素的數據結構。字典的一個重要特征是更快的查找; 您可以添加或刪除選項而不會產生性能開銷。

.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篇了,目前兩到三天更新一篇。最后感謝大家的閱讀。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM