1.泛型的概念
C#中的泛型與C++中的模板類似,泛型是實例化過程中提供的類型或類建立的。泛型並不限於類,還可以創建泛型接口、泛型方法,甚至泛型委托。這將極大提高代碼的靈活性,正確使用泛型可以顯著縮短開發時間。與C++不同的是,C#中所有操作都是在運行期間進行的。
2.使用泛型
- 可空類型
值類型必須包含一個值,它們可以在聲明之后,賦值之前,在未賦值狀態下存在,但不能以任何方式使用,而引用類型可以為null。有時讓值類型為空是很有用的,泛型提供了使用System.Nullable<T>使值類型為空的一種方式。如下代碼:
private Nullable<int> _nullableInt;
則可以為_nullableInt賦為null,如下:
_nullableInt = null;
可空類型非常有用,以致於C#中加入了語法:
int? _nullableIntSecond;
- System.Collections.Generic 名稱空間
這個名稱空間用於處理集合的泛型類型,使用的非常頻繁。將以List<T>和Dictionary<K,V>為例介紹這些類,以及和它們配合使用的接口和方法。
1.List<T>
List<T>泛型集合類更加快捷,更易於使用,創建T類型對象的集合需要一下方法:
List<string> _myCollection = new List<string>();
將創建T為String的List集合。
可以在代碼中去查看List<T>所支持的方法,這里不再贅述。
2.對泛型列表進行排序和搜索
對泛型列表進行排序和搜索與和其它列表進行排序和搜索是一樣的,如下為實例代碼:
public class NumberCollection : List<int> { public NumberCollection(IEnumerable<int> initialNums) { foreach (var num in initialNums) { Add(num); } } public NumberCollection() { for (int i=0;i < 10;++i) { Add(i); } } public void Print() { foreach (var num in this) { Console.WriteLine(num); } Console.Read(); } }
public static class NumberCollectionDelegate { public static int Compare(int i,int j) { if (i > j) { return -1; } else if (i < j) { return 1; } return 0; } public static bool Find(int i) { if (i %2 == 0) { return true; } return false; }
public static Comparison<int> CopmareDelegate = new Comparison<int>(Compare);
public static Predicate<int> Predicate = new Predicate<int>(Find);
}
var numColleciton = new NumberCollection(); numColleciton.Print(); numColleciton.Sort(NumberCollectionDelegate.CopmareDelegate); numColleciton.Print(); var newNumCollection = new NumberCollection(numColleciton.FindAll(NumberCollectionDelegate.Predicate)); newNumCollection.Print(); Console.ReadLine();
如上代碼,首先定義了NumberCollection繼承自List<int>,定義了Print方法用來輸出集合中所有的值,類NumberCollectionDelegate中定義了Compare方法,以及Find方法,Compare方法用於對集合進行降序排序(為什么這么寫是降序排序請關注另一篇文章),Find方法用於選擇集合中為偶數的值。在實例的使用中,首先對集合中的元素進行了降序排序,后選擇集合中為偶數的值組成新的集合,當然上述比較與查找用法可以簡化為以下用法:
numColleciton.Sort(NumberCollectionDelegate.Compare);
var newNumCollection = new NumberCollection(numColleciton.FindAll(NumberCollectionDelegate.Find));
這樣就不需要顯示引用Comparison<int>類型了,但是在使用時仍然會隱式創建Comparison<int>實例,對於比較也是同樣的。在許多情況下,都可以使用方法組以這種方式隱式的創建委托,使代碼變的更容易讀取。
3.Dictionary<K,V>
這個類型可以定義鍵值對的集合,這個類型需要實例化兩個類型,分別用於鍵和值,以表示集合中的各個項。可以使用強類型化的Add方法添加鍵值對,如下。
Dictionary<string, int> stringIntDictionary = new Dictionary<string, int>();
stringIntDictionary.Add("Tom", 1);
stringIntDictionary.Add("Lucy", 2);
stringIntDictionary.Add("Lily", 3);
可以直接訪問Dicitionary中的keys和Values屬性值:
foreach (var key in stringIntDictionary.Keys)
{
Console.WriteLine(key);
}
foreach (var value in stringIntDictionary.Values)
{
Console.WriteLine(value);
}
同樣可以迭代集合中每一項,如下:
foreach (KeyValuePair<string,int> item in stringIntDictionary)
{
Console.WriteLine("{0} = {1}", item.Key, item.Value);
}
對於Dictionary<K,V>需要注意的一點是,每個項的鍵都必須式唯一的。如果要添加的項與已存在的項的鍵值相同,則會拋出異常。
3.定義泛型
1.定義泛型類
要創建泛型類,只需在類定義中包括尖括號:
class MyGenericClass<T>
{
}
其中T可以是任意標識符,只需要遵循C#命名規則即可,但一般只使用T。
泛型類可以在其定義中包含任意多個類型,它們用逗號分開,例如:
class MyGenericClass<T1,T2,T3>
{
}
定義了這些類型之后,就可以在類定義中像使用其它類型那樣使用它們,如下
class MyGenericClass<T1,T2,T3> { private T1 _object; public MyGenericClass(T1 item) { _object = item; } public T1 InnerT1Object { get { return _object; } } }
注意不能假定使用了什么類型,例如:
_object = new T1();
因為此刻不知道T1是什么,也就不能使用它的構造函數,可能T1就沒有構造函數,或者沒有可公共訪問的構造函數。因此要對泛型進行實際的操作需要更多了解其使用的類型。
- default關鍵字
要確定用於創建泛型類型的實例,需要了解一個最基本的情況,它是引用類型還是值類型,若不了解這個情況就不能直接對變量賦予null值。此時default關鍵字就派上了用場:
_object = default(T1);
如果_object是引用類型就給引用類型賦為null值,如果為值類型就給它賦為默認值。對於數字類型默認值為0,對於結構,按照相同的規則對它們進行賦值。default關鍵字允許對必須使用的類型進行更多的操作,為了進行更多的操作,必須對使用的類型進行更多的約束。
- 約束類型
前面使用的類型稱為無綁定類型,因為沒有對它們進行任何約束,而通過約束可以限定用於泛型的類型。在類定義中,可以使用where關鍵字,來限定用於泛型的類型,如下:
class MyGenericClass<T1,T2,T3> where T1 : constraint
其中constraint定義了約束,可以用這種能方式定義很多約束,每個約束之間用逗號分開。還可以使用多個where語句,定義泛型類型需要的任意類型或所有類型上的約束,約束必須出現在類型說明符的后面。
- 從泛型類中繼承
類可以從泛型中繼承,如
class Farm<T> : IEnumerable<T> where T : Animal
{
}
如上代碼Farm<T>是一個接口類型,同樣對於T的約束也會在IEnumerable中使用的T上添加一個額外的約束,這可以用於限制用於約束的類型,但是需要遵守一些規則。
首先,如果某個類型所繼承的基類型中受到了約束,該類型就不能接觸約束,即類型T在基類中使用時所受到的約束,必須擴展到子類中,至少於基類的約束相同。
- 泛型運算符
泛型類也支持運算符的重寫。
- 泛型結構
結構與類相同,只是有一些細微的差別,而且結構是值類型,不是引用類型,所以可以創建泛型結構,如:
struct MyGenericStruct<T1, T2>
{
}
2.定義泛型接口
定義泛型接口於定義泛型類所用的技術相同,例如:
interface IGeneric<T> where T : Object { void Sum(T x, T y); }
3.定義泛型方法
可以使用泛型方法以達到泛型方法的更一般形式,在泛型方法中,參數類型或返回類型由泛型類型參數所決定。
,例如:
T GetDefault<T>() { return default(T); }
可以使用非泛型類,實現泛型方法:
public class Defaulter { public T GetDefault<T>() { return default(T); } }
如果類是泛型的,那么需要為類中的泛型方法提供不同的標示符。如下代碼會提示泛型方法:
public class Defaulter<T> { public T GetDefault<T>() { return default(T); } }
會提示內部泛型參數與外部泛型參數相同,此時應該更改泛型標示符。
本篇內容參考C#入門經典。