泛型通常用在集合和集合上運行的方法中; 泛型NET Framework2.0提供一個新的命名空間System。Collections。Generic。 早期集合缺點: System.Collections.ArrayList list = new System.Collections.ArrayLi
泛型通常用在集合和集合上運行的方法中;
泛型NET Framework2.0提供一個新的命名空間System。Collections。Generic。
早期集合缺點:
System.Collections.ArrayList list = new System.Collections.ArrayList(); // Add an integer to the list. list.Add(3); // Add a string to the list. This will compile, but may cause an error later. list.Add("It is raining in Redmond.");
添加到 ArrayList 中的任何引用或值類型都將隱式地向上強制轉換為 Object。如果項是值類型,則必須在將其添加到列表中時進行裝箱操作,在檢索時進行取消裝箱操作。強制轉換以及裝箱和取消裝箱操作都會降低性能;在必須對大型集合進行迴圈訪問的情況下,裝箱和取消裝箱的影響非常明顯。(泛型的優點)
// The .NET Framework 2.0 way to create a list List<int> list1 = new List<int>(); // No boxing, no casting: list1.Add(3); // Compile-time error: // list1.Add("It is raining in Redmond.");
優點:“對於客戶端代碼,與 ArrayList 相比,使用 List<T> 時添加的唯一語法是聲明和實例化中的類型參數。雖然這稍微增加了些編碼的複雜性,但好處是您可以創建一個比 ArrayList 更安全並且速度更快的列表,特別適用於列表項是值類型的情況.
在泛型方法中,類型參數是客戶端在實例化泛型類型的變數時指定的特定類型的占位符。
類型參數命名準則:
- 儘量使用描述性名稱命名,除非單個字母完全可以讓人理解它表示的含義。
- 考慮使用T作為單個字母類型參數的類型的類型參數。
- 務必將T 作為泛型參數描述性的首碼。如TSession
- 考慮在參數名中指示對此類型參數的約束。例如,可以將帶有 ISession 約束的參數命名為 TSession。
類型參數的約束
T:結構 類型參數必須是值類型,可以指定除Nullable以外的任何值類型。
public class MyClass2<T> where T : struct
//這個泛型類只接受值類型的泛型參數
T:類 類型 參數必須是引用類型,包括任何類,介面。委托。數組類型。
public class MyClass<T> where T:class//這個泛型類只接受引用類型的泛型參數
T:NEW() 類型參數必須具有無參數的公共構造函數,當與其他約束一起使用時,new()約
束必須最後指定。
public class MyClass3<T> where T : new()
T:<基類名>:類型參數必須是指定的基類或派生自指定的基類。
public class MyClass5<T>where T : Customer
T:<介面名稱,類型參數必須是指定的介面或者實現指定的介面。可以指定多個介面約束,約束介面也可以是泛型的>
public class MyClass4<T> where T : System.IComparable
T:U。。。。為 T 提供的類型參數必須是為 U 提供的參數或派生自為 U 提供的參數。這稱為裸類型約束。
在指定一個類型參數時,可以指定類型參數必須滿足的約束條件。
這是通過在指定類型參數時使用where子句來實現的。
class
class-name<type-param> where type-param:constraints{}
在泛型中,要為某個使用泛型的變數初始化值,
可是我們需要考慮的是這個泛型可能是引用類型,也可能是值類型,這時我們可以藉助default來完成初始化複製。
T value = default(T);如果T是引用類型,value = null,如果是T是值類型,value = 0.
泛型類
class Test<T> { public T obj; public Test(T obj) { this.obj = obj; } }
子類繼承泛型類時必須明確指定泛型參數的類型
當子類也是泛型參數是,要註意約束必須和父類的匹配
class C<U,V> class D:C<string,int> class E<U,V>:C<U,V> class F<U,V>:C<string,int>
泛型介面
//定義泛型介面 interface Icomuter<T> { //定義泛型方法 T Add(T item1, T item2); T Substract(T item1, T item2); } //繼承泛型介面 class Student : Icomuter<int> { //繼承實現泛型方法 public int Add(int item1, int item2) { return item1 + item2; } public int Substract(int item1, int item2) { return item1 - item2; } }
public interface IList< T> { T[] GetElements(); } public interface IDictionary< K,V> { void Add(K key, V value); } // 泛型介面的類型參數要麼已實例化 // 要麼來源於實現類聲明的類型參數 class List< T> : IList< T>, IDictionary< int, T> { public T[] GetElements() { return null; } public void Add(int index, T value) {} }
泛型方法
泛型方法是使用類型參數聲明的方法
static void Swap<T>(ref T lhs, ref T rhs) { T temp; temp = lhs; lhs = rhs; rhs = temp; } //方法的調用 int a = 1, b = 2; Swap<int>(ref a, ref b); System.Console.WriteLine(a + " " + b); //省略類型參數,編譯器將推斷出該參數 Swap(ref a, ref b); //相同的類型推斷規則也適用於靜態方法以及實例方法。編譯器能夠根據傳入的方法參數推斷類型參數;它無法僅從約束或返回值推斷類型參數
在泛型類中,非泛型方法可以訪問類級別類型參數,如下所示:
class SampleClass<T> { void Swap(ref T lhs, ref T rhs) { } }
泛型方法是使用類型參數聲明的方法,如下所示:
static void Swap<T>(ref T lhs, ref T rhs) { T temp; temp = lhs; lhs = rhs; rhs = temp; } public static void TestSwap() { int a = 1; int b = 2; Swap<int>(ref a, ref b); System.Console.WriteLine(a + " " + b); }View Code
也可以省略類型參數,編譯器將推斷出該參數。下麵對 Swap 的調用等效於前面的調用:
Swap(ref a, ref b);
相同的類型推斷規則也適用於靜態方法以及實例方法。編譯器能夠根據傳入的方法參數推斷類型參數;它無法僅從約束或返回值推斷類型參數。因此,類型推斷不適用於沒有參數的方法。類型推斷在編譯時、編譯器嘗試解析任何重載方法簽名之前進行。編譯器向共用相同名稱的所有泛型方法應用類型推斷邏輯。在重載解析步驟中,編譯器僅包括類型推斷取得成功的那些泛型方法。
在泛型類中,非泛型方法可以訪問類級別類型參數,如下所示:
class SampleClass<T> { void Swap(ref T lhs, ref T rhs) { } }
如果定義的泛型方法接受與包含類相同的類型參數,編譯器將生成警告 CS0693,因為在方法範圍內,為內部 T 提供的參數將隱藏為外部 T 提供的參數。除了類初始化時提供的類型參數之外,如果需要靈活調用具有類型參數的泛型類方法,請考慮為方法的類型參數提供其他標識符,如下麵示例中的GenericList2<T> 所示。
class GenericList<T> { // CS0693 void SampleMethod<T>() { } } class GenericList2<T> { //No warning void SampleMethod<U>() { } }
使用約束對方法中的類型參數啟用更專門的操作。此版本的 Swap<T> 現在稱為 SwapIfGreater<T>,它只能與實現 IComparable<T> 的類型參數一起使用
void SwapIfGreater<T>(ref T lhs, ref T rhs) where T : System.IComparable<T> { T temp; if (lhs.CompareTo(rhs) > 0) { temp = lhs; lhs = rhs; rhs = temp; } }
在 C# 2.0 中,下限為零的一維數組自動實現 IList<T>。這使您可以創建能夠使用相同代碼迴圈訪問數組和其他集合類型的泛型方法。此技術主要對讀取集合中的數據很有用。IList<T> 介面不能用於在數組中添加或移除元素;如果試圖在此上下文中調用 IList<T> 方法(如數組的 RemoveAt),將引發異常。
//下麵的代碼示例演示帶有 IList<T> 輸入參數的單個泛型方法如何同時迴圈訪問列表和數組,本例中為整數數組。 class Program { static void Main() { int[] arr = { 0, 1, 2, 3, 4 }; List<int> list = new List<int>(); for (int x = 5; x < 10; x++) { list.Add(x); } ProcessItems<int>(arr); ProcessItems<int>(list); } static void ProcessItems<T>(IList<T> coll) { foreach (T item in coll) { System.Console.Write(item.ToString() + " "); } System.Console.WriteLine(); } } //儘管 ProcessItems 方法無法添加或移除項,但對於 ProcessItems 內部的 T[],IsReadOnly 屬性返回 False,因為該數組本身未聲明 ReadOnly 特性。View Code
委托 可以定義自己的類型參數。引用泛型委托的代碼可以指定類型參數以創建已關閉的構造類型,就像實例化泛型類或調用泛型方法一樣,如下例所示:
public delegate void Del<T>(T item); public static void Notify(int i) { } Del<int> m1 = new Del<int>(Notify);View Code
C# 2.0 版具有稱為方法組轉換的新功能,此功能適用於具體委托類型和泛型委托類型,並使您可以使用如下簡化的語法寫入上一行:
Del<int> m2 = Notify;
在泛型類內部定義的委托使用泛型類類型參數的方式可以與類方法所使用的方式相同。
class Stack<T> { T[] items; int index; public delegate void StackDelegate(T[] items); }View Code
引用委托的代碼必須指定包含類的類型變數,如下所示:
private static void DoWork(float[] items) { } public static void TestStack() { Stack<float> s = new Stack<float>(); Stack<float>.StackDelegate d = DoWork; }View Code
根據典型設計模式定義事件時,泛型委托尤其有用,因為發送方參數可以為強類型,不再需要強制轉換成 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; }View Code
default:
之所以會用到default關鍵字,是因為需要在不知道類型參數為值類型還是引用類型的情況下,為對象實例賦初值。
在泛型類和泛型方法中產生的一個問題是,在預先未知以下情況時,如何將預設值分配給參數化類型 T:
-
T 是引用類型還是值類型。
如果 T 為值類型,則它是數值還是結構