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

寫在前面

今天忙裏偷閒在瀏覽外文的時候看到一篇講C#中泛型的使用的文章,所以加上本人的理解以及四級沒過的英語水平斗膽給大夥進行了翻譯,固然在翻譯的過程當中發現了一些問題,所以也進行了糾正,固然,原文的地址我放在最下面,若是你的英文水平比較好的話,能夠直接直接閱讀全文。同時最近建了一個.NET Core實戰項目交流羣637326624,有興趣的朋友能夠來相互交流。目前.NET Core實戰項目之CMS的教程也已經更新了6篇了,目前兩到三天更新一篇。html

做者:依樂祝
原文地址:http://www.javashuo.com/article/p-ytlwppbl-gq.html算法

介紹

C#和.NET中的泛型程序具備強類型集合的許多優勢,併爲代碼提供更高質量和性能提高。泛型是C#語言和公共語言運行庫(CLR)中的一個新功能,它將類型參數的概念引入.NET Framework。類型參數使得設計某些類和方法成爲可能,例如,經過使用泛型類型參數T,能夠大大簡化類型之間的強制轉換或裝箱操做的過程(裝箱、拆箱問題)。說白了,泛型就是經過參數化類型來實如今同一份代碼上操做多種數據類型,利用「參數化類型」將類型抽象化,從而實現靈活的複用。每一個集合的詳細規範能夠在System.Collection.Generic名稱空間下找到。編程

通用類C#

裝箱和拆箱

.Net定義了兩種主要的數據類型來表示變量,也就是傳說中的值類型和引用類型。這是須要裝箱和拆箱的地方。裝箱是一種經過將變量存儲到System.Object中來顯式地將值類型轉換爲引用類型的機制。當您裝入值時,CLR會將新對象分配到堆中,並將值類型的值複製到該實例中。例如,您建立了一個int類型的變量:c#

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執行了一系列操做,例如,首先在託管堆中分配一個對象,而後在裝箱中將值轉換爲內存位置,並在拆箱期間將值存儲在堆上而且必須轉回到堆棧。所以,從性能的角度來看,裝箱和拆箱過程在泛型中具備很是重要的意義,由於這個過程若是不使用泛型的話會耗費更多地資源。ide

泛型類

能夠經過在類名後面加上 符號來定義泛型類。這裏沒有強制必須將「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篇了,目前兩到三天更新一篇。最後感謝你們的閱讀。

相關文章
相關標籤/搜索