泛型是 2.0 版 C# 語言和公共語言運行庫 (CLR) 中的一個新功能。泛型將類型參數的概念引入 .NET Framework,類型參數使得設計以下類和方法成爲可能:這些類和方法將一個或多個類型的指定推遲到客戶端代碼聲明並實例化該類或方法的時候。例如,經過使用泛型類型參數 T,您能夠編寫其餘客戶端代碼可以使用的單個類,而不致引入運行時強制轉換或裝箱操做的成本或風險html
在泛型類型或泛型方法的定義中,類型參數是一個佔位符(placeholder),一般爲一個大寫字母,如T。在客戶代碼聲明、實例化該類型的變量時,把 T替換爲客戶代碼所指定的數據類型。 說到泛型就不得不說說object,list,var,dynamic,這三個是依次出現的,都是時代進步的產物。node
> 優勢:
> 1. object類型能夠用來引用任何類型的實例;
> 2. object類型能夠存儲任何類型的值;
> 3. 能夠定義object類型的參數;
> 4. 能夠把object做爲返回類型。 程序員
> 缺點:
> 1. 會由於程序員沒有記住使用的類型而出錯,形成類型不兼容;
> 2. 值類型和引用類型的互化即裝箱拆箱使系統性能降低。編程
> 1. 性能高:定義數據類型,不須要類型轉換,避免拆裝箱帶來的性能損失;
> 2. 類型安全:定義容許使用的數據類型,在編譯時檢查類型錯誤,及早發現錯誤。
> 3. 泛型是程序編譯的時候,他會把它編譯成所須要的類型,主要是減小了代碼量,方便程序員。設計模式
var其實是編譯期拋給咱們的「語法糖」,一旦被編譯,編譯期會自動匹配var 變量的實際類型,並用實際類型來替換該變量的申明,這看上去就好像咱們在編碼的時候是用實際類型進行申明的,vs中鼠標放上取得時候會顯示類型。數組
dynamic是FrameWork4.0的新特性。dynamic的出現讓C#具備了弱語言類型的特性。編譯器在編譯的時候再也不對類型進行檢查,編譯期默認dynamic對象支持你想要的任何特性。
而dynamic被編譯後,實際是一個object類型,只不過編譯器會對dynamic類型進行特殊處理,讓它在編譯期間不進行任何的類型檢查,而是將類型檢查放到了運行期。vs中能夠看出來,object的點上去仍是object,var點上去是須要的類型,dynamic點上去dynamic。安全
object 下面的步驟會保存,obj編譯的時候就不會經過,而dynamic則會繞過編譯器,運行的時候去解析,運行的時候dynamic是不存在的。var下面也是能夠經過的,鼠標點上去會是int型。app
``` C# var testInt = 1; testInt = testInt + 1; ```
在定義泛型類時,能夠對客戶端代碼可以在實例化類時用於類型參數的類型種類施加限制。若是客戶端代碼嘗試使用某個約束所不容許的類型來實例化類,則會產生編譯時錯誤。這些限制稱爲約束。約束是使用 where 上下文關鍵字指定的。下表列出了六種類型的約束:less
約束 | 說明 |
---|---|
T:結構ide |
類型參數必須是值類型。能夠指定除 Nullable 之外的任何值類型。有關更多信息,請參見使用可空類型(C# 編程指南)。 |
T:類 |
類型參數必須是引用類型,包括任何類、接口、委託或數組類型。 |
T:new() |
類型參數必須具備無參數的公共構造函數。當與其餘約束一塊兒使用時,new() 約束必須最後指定。 |
T:<基類名> |
類型參數必須是指定的基類或派生自指定的基類。 |
T:<接口名稱> |
類型參數必須是指定的接口或實現指定的接口。能夠指定多個接口約束。約束接口也能夠是泛型的。 |
T:U |
爲 T 提供的類型參數必須是爲 U 提供的參數或派生自爲 U 提供的參數。這稱爲裸類型約束。 |
舉例:
class EmployeeList<T> where T : Employee, IEmployee, System.IComparable<T>, new() { // ... }
class SuperKeyType<K, V, U> where U : System.IComparable<U> where V : new() { }
泛型類最經常使用於集合,如連接列表、哈希表、堆棧、隊列、樹等,其中,像從集合中添加和移除項這樣的操做都以大致上相同的方式執行,與所存儲數據的類型無關。
通常狀況下,建立泛型類的過程爲:從一個現有的具體類開始,逐一將每一個類型更改成類型參數,直至達到通用化和可用性的最佳平衡。建立您本身的泛型類時,須要特別注意如下事項:
將哪些類型通用化爲類型參數。
通常規則是,可以參數化的類型越多,代碼就會變得越靈活,重用性就越好。可是,太多的通用化會使其餘開發人員難以閱讀或理解代碼。
若是存在約束,應對類型參數應用什麼約束
一個有用的規則是,應用盡量最多的約束,但仍使您可以處理須要處理的類型。例如,若是您知道您的泛型類僅用於引用類型,則應用類約束。這能夠防止您的類被意外地用於值類型,並容許您對 T 使用 as 運算符以及檢查空值。
是否將泛型行爲分解爲基類和子類。
因爲泛型類能夠做爲基類使用,此處適用的設計注意事項與非泛型類相同。有關從泛型基類繼承的規則,請參見下面的內容。
是否實現一個或多個泛型接口。
例如,若是您設計一個類,該類將用於建立基於泛型的集合中的項,則可能須要實現一個接口,如 IComparable<T>,其中 T 是您的類的類型。
爲泛型集合類或表示集合中項的泛型類定義接口一般頗有用。對於泛型類,使用泛型接口十分可取,例如使用 IComparable<T> 而不使用 IComparable,這樣能夠避免值類型的裝箱和取消裝箱操做。.NET Framework 2.0 類庫定義了若干新的泛型接口,以用於 System.Collections.Generic 命名空間中新的集合類。
舉例:
//Type parameter T in angle brackets. public class GenericList<T> : System.Collections.Generic.IEnumerable<T> { protected Node head; protected Node current = null; // Nested class is also generic on T protected class Node { public Node next; private T data; //T as private member datatype public Node(T t) //T used in non-generic constructor { next = null; data = t; } public Node Next { get { return next; } set { next = value; } } public T Data //T as return type of property { get { return data; } set { data = value; } } } public GenericList() //constructor { head = null; } public void AddHead(T t) //T as method parameter type { Node n = new Node(t); n.Next = head; head = n; } // Implementation of the iterator public System.Collections.Generic.IEnumerator<T> GetEnumerator() { Node current = head; while (current != null) { yield return current.Data; current = current.Next; } } // IEnumerable<T> inherits from IEnumerable, therefore this class // must implement both the generic and non-generic versions of // GetEnumerator. In most cases, the non-generic method can // simply call the generic method. System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } } public class SortedList<T> : GenericList<T> where T : System.IComparable<T> { // A simple, unoptimized sort algorithm that // orders list elements from lowest to highest: public void BubbleSort() { if (null == head || null == head.Next) { return; } bool swapped; do { Node previous = null; Node current = head; swapped = false; while (current.next != null) { // Because we need to call this method, the SortedList // class is constrained on IEnumerable<T> if (current.Data.CompareTo(current.next.Data) > 0) { Node tmp = current.next; current.next = current.next.next; tmp.next = current; if (previous == null) { head = tmp; } else { previous.next = tmp; } previous = tmp; swapped = true; } else { previous = current; current = current.next; } } } while (swapped); } } // A simple class that implements IComparable<T> using itself as the // type argument. This is a common design pattern in objects that // are stored in generic lists. public class Person : System.IComparable<Person> { string name; int age; public Person(string s, int i) { name = s; age = i; } // This will cause list elements to be sorted on age values. public int CompareTo(Person p) { return age - p.age; } public override string ToString() { return name + ":" + age; } // Must implement Equals. public bool Equals(Person p) { return (this.age == p.age); } } class Program { static void Main() { //Declare and instantiate a new generic SortedList class. //Person is the type argument. SortedList<Person> list = new SortedList<Person>(); //Create name and age values to initialize Person objects. string[] names = new string[] { "Franscoise", "Bill", "Li", "Sandra", "Gunnar", "Alok", "Hiroyuki", "Maria", "Alessandro", "Raul" }; int[] ages = new int[] { 45, 19, 28, 23, 18, 9, 108, 72, 30, 35 }; //Populate the list. for (int x = 0; x < 10; x++) { list.AddHead(new Person(names[x], ages[x])); } //Print out unsorted list. foreach (Person p in list) { System.Console.WriteLine(p.ToString()); } System.Console.WriteLine("Done with unsorted list"); //Sort the list. list.BubbleSort(); //Print out sorted list. foreach (Person p in list) { System.Console.WriteLine(p.ToString()); } System.Console.WriteLine("Done with sorted list"); } }
泛型方法是使用類型參數聲明的方法,以下所示:
static void Swap<T>(ref T lhs, ref T rhs) { T temp; temp = lhs; lhs = rhs; rhs = temp; }
根據典型設計模式定義事件時,泛型委託尤爲有用,由於發送方參數能夠爲強類型,再也不須要強制轉換成 Object,或反向強制轉換。
delegate void StackEventHandler<T, U>(T sender, U eventArgs); class Stack<T> { public class StackEventArgs : System.EventArgs { } public event StackEventHandler<Stack<T>, StackEventArgs> stackEvent; protected virtual void OnStackChanged(StackEventArgs a) { stackEvent(this, a); } } class SampleClass { public void HandleStackChange<T>(Stack<T> stack, Stack<T>.StackEventArgs args) { } } public static void Test() { Stack<double> s = new Stack<double>(); SampleClass o = new SampleClass(); s.stackEvent += o.HandleStackChange; }
下面是鏈表
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; /** * 鏈表是一種物理存儲單元上非連續、非順序的存儲結構,數據元素的邏輯順序是經過鏈表中的指針連接次序實現的。 * 鏈表由一系列結點(鏈表中每個元素稱爲結點)組成,結點能夠在運行時動態生成。 * 每一個結點包括兩個部分:一個是存儲數據元素的數據域,另外一個是存儲下一個結點地址的指針域。 * 相比於線性表順序結構,操做複雜。 * **/ namespace NETTest { public class NodeTable { } /// <summary> /// 這個類僅包含一個表示員工名字的字符串類型,一個設置員工名字的構造函數,一個返回Employee名字的ToString()方法。 /// </summary> public class Employee { private string name; public Employee(string name) { this.name = name; } public override string ToString() { return this.name; } } public class Node<T> { Object data; Node<T> next; //注意構造函數將私有的數據成員設置成傳遞進來的對象,而且將 next 字段設置成null。 public Node(Object data) { this.data = data; this.next = null; } public Object Data { get { return this.data; } set { data = value; } } public Node<T> Next { get { return this.next; } set { this.next = value; } } /// <summary> /// 首先檢測當前Node的next字段,看它是否是null。 /// 若是是,那麼當前Node就是最後一個Node,咱們將當前Node的next屬性指向傳遞進來的新結點, /// 這樣,咱們就把新Node插入到了鏈表的尾部。 /// 若是當前Node的next字段不是null,說明當前node不是鏈表中的最後一個node。 /// 由於next字段的類型也是node,因此咱們調用next字段的Append方法(注:遞歸調用),再一次傳遞Node參數,這樣繼續下去,直到找到最後一個Node爲止。 /// </summary> /// <param name="newNode"></param> public void Append(Node<T> newNode) { if (this.next == null) { this.next = newNode; } else { next.Append(newNode); } } /// <summary> /// Node 類中的 ToString() 方法也被覆蓋了,用於輸出 data 中的值,而且調用下一個 Node 的 ToString()方法(譯註:再一次遞歸調用)。 /// </summary> /// <returns></returns> public override string ToString() { string output = data.ToString(); if (next != null) { output += ", " + next.ToString(); } return output; } } /** * LinkedList 類自己只包含對一個Node的引用,這個Node稱做 HeadNode,是鏈表中的第一個Node,初始化爲null。 * **/ public class LinkedList<T> { Node<T> headNode = null; /// <summary> /// LinkedList 類不須要構造函數(使用編譯器建立的默認構造函數), /// 可是咱們須要建立一個公共方法,Add(),這個方法把 data存儲到線性鏈表中。 /// 這個方法首先檢查headNode是否是null, /// 若是是,它將使用data建立結點,並將這個結點做爲headNode, /// 若是不是null,它將建立一個新的包含data的結點,並調用headNode的Append方法 /// </summary> /// <param name="data"></param> public void Add(Object data) { if (headNode == null) { headNode = new Node<T>(data); } else { headNode.Append(new Node<T>(data)); } } /// <summary> /// 線性鏈表建立一個索引器。 /// </summary> /// <param name="index"></param> /// <returns></returns> public object this[int index] { get { int ctr = 0; Node<T> node = headNode; while (node != null && ctr <= index) { if (ctr == index) { return node.Data; } else { node = node.Next; } ctr++; } return null; } } /// <summary> /// 用以調用headNode的ToString()方法。 /// </summary> /// <returns></returns> public override string ToString() { if (this.headNode != null) { return this.headNode.ToString(); } else { return string.Empty; } } } }
static void Main(string[] args) { /**泛型**/ LinkedList<int> ll = new LinkedList<int>(); for (int i = 0; i < 10; i++) { ll.Add(i); } Console.Write(ll); Console.WriteLine(" Done."); LinkedList<Employee> employees = new LinkedList<Employee>(); employees.Add(new Employee("Jason1")); employees.Add(new Employee("Jason2")); employees.Add(new Employee("Jason3")); employees.Add(new Employee("Jason4")); Console.Write(employees); Console.WriteLine(" Done."); Console.WriteLine("The fourth integer is {0}.", ll[3]); Employee d = (Employee)employees[1]; Console.WriteLine("The second Employee is {0}.", d); Console.ReadLine(); }
5. C#中泛型的解釋(object,list,var,dynamic的區別)
6. C#中委託
7. C#和.NET版本對比