C# 2.0 - 泛型(Generics)
除非必要,否則請不要使用泛型(Generics)!濫用泛型隻會增加代碼的複雜性。
有如“Hello, World!”一樣,說到泛型必定從Stack說起,先看看。
public class Stack <T>
{
public void Push(T item)
{...}
public T Pop()
{...}
}
Stack<int> stack = new Stack<T>();
stack.Push(12);
int i = stack.Pop();
{
public void Push(T item)
{...}
public T Pop()
{...}
}
Stack<int> stack = new Stack<T>();
stack.Push(12);
int i = stack.Pop();
注:微軟中文文檔中把T翻譯為“一般類型參數”。???我覺得還是翻譯為“類型參數”更合適些。
如果你熟悉 .net Framework 的容器類,必定能感覺到泛型帶來的好處。
1. 同樣支持任意類型,包括引用類型和值類型。
2. 實現了類型安全檢查,包括IDE編碼檢查和編譯器檢查。
3. 避免了值類型的裝箱和拆箱,提高了性能。
泛型的好處
(摘自MSDN China)
.NET 中的泛型使您可以重用代碼以及在實現它時付出的努力。類型和內部數據可以在不導致代碼膨脹的情況下更改,而不管您使用的是值類型還是引用類型。您可以一次性地開發、測試和部署代碼,通過任何類型(包括將來的類型)來重用它,並且全部具有編譯器支持和類型安全。因為一般代碼不會強行對值類型進行裝箱和取消裝箱,或者對引用類型進行向下強製類型轉換,所以性能得到顯著提高。對於值類型,性能通常會提高 200%;對於引用類型,在訪問該類型時,可以預期性能最多提高 100%(當然,整個應用程序的性能可能會提高,也可能不會提高)。
泛型同樣適用於Struct
public struct Pos <T>
{
public T x;
public T y;
}
{
public T x;
public T y;
}
多個類型參數
public class List<K, T>
{
public void Add(K key, T value)
{...}
public T Items[K key]
{
get {...}
}
}
List<string, int> list = new List<string, int>();
list.Add("a", 1);
Console.WriteLine(list["a"]);
{
public void Add(K key, T value)
{...}
public T Items[K key]
{
get {...}
}
}
List<string, int> list = new List<string, int>();
list.Add("a", 1);
Console.WriteLine(list["a"]);
派生約束
有些時候我們需要對類型參數進行約束,比如要求T必須實現某個接口或者繼承自某個類,以便我們在方法中調用某個方法。
public class Class1<T> where T : IComparable
{
public void Method(T t)
{
t.CompareTo(...);
}
}
public class Class2<K, T> : ClassX
where T : ClassX, IComparable
where K : IComparable
{
public void Method(T t)
{
t.CompareTo(...);
}
}
{
public void Method(T t)
{
t.CompareTo(...);
}
}
public class Class2<K, T> : ClassX
where T : ClassX, IComparable
where K : IComparable
{
public void Method(T t)
{
t.CompareTo(...);
}
}
構造函數約束
要求類型參數必須擁有默認構造方法,以便我們在泛型類中創建該類型對象。
public class GenericsClass <T>
where T : new()
{
}
where T : new()
{
}
引用/值類型約束
要求類型參數必須是值類型或者引用類型。
public class GenericsClass<T>
where T : struct
where K : class
{
}
where T : struct
where K : class
{
}
泛型和強製類型轉換
編譯器允許您將類型參數顯式強製轉換到其他任何接口,但不能將其轉換到類。
class MyClass <T>
{
void SomeMethod(T t)
{
ISomeInterface obj1 = (ISomeInterface)t;//Compiles
SomeClass obj2 = (SomeClass)t; //Does not compile
}
}
但是,您可以使用臨時的 Object 變量,將類型參數強製轉換到其他任何類型。
{
void SomeMethod(T t)
{
ISomeInterface obj1 = (ISomeInterface)t;//Compiles
SomeClass obj2 = (SomeClass)t; //Does not compile
}
}
class MyClass<T>
{
void SomeMethod(T t)
{
object temp = t;
SomeClass obj = (SomeClass)temp;
}
}
這樣的顯式強製類型轉換是危險的,因為如果為取代類型參數而使用的類型實參不是派生自您要顯式強製轉換到的類型,則可能在運行時引發異常。要想不冒引發強製類型轉換異常的危險,一種更好的辦法是使用 is 和 as 運算符。
{
void SomeMethod(T t)
{
object temp = t;
SomeClass obj = (SomeClass)temp;
}
}
public class MyClass <T>
{
public void SomeMethod(T t)
{
if(t is int)
{...}
if(t is LinkedList)
{...}
string str = t as string;
if(str != null)
{...}
LinkedList list = t as LinkedList;
if(list != null)
{...}
}
}
{
public void SomeMethod(T t)
{
if(t is int)
{...}
if(t is LinkedList)
{...}
string str = t as string;
if(str != null)
{...}
LinkedList list = t as LinkedList;
if(list != null)
{...}
}
}
最後更新:2017-04-02 00:06:28