泛型的优势
假設需要一個兩個整形變量交換的函數,我們很快就可以嗒嗒嗒嗒的敲出下面的 Swap 函數:
?
void Swap(ref int lhs, ref int rhs)
{
??? int temp = lhs;
??? lhs = rhs;
??? rhs = temp;
}
?
隨著項目進展,我們發現,需要用到 Swap 函數的不僅是整形,變量 還有字符串,于是我們我又嗒嗒嗒嗒的重載 Swap 函數如下:
?
void Swap(ref string lhs, ref string rhs)
{
??? string temp = lhs;
??? lhs = rhs;
??? rhs = temp;
}
?
接下來的開發中,我們又發現還有自定義的結構體,類等等等等都要用到 Swap 函數。如果我們為每一種類型都實現一個相應的 Swap 函數的話,各個版本的 Swap 函數數據類型不同外,其它完完全全一樣。也就是說,項目中存在大量的代碼重復。能不能之實現一個能夠適用于不同數據類型的 Swap 函數,消除這種代碼冗余,從而減少工作量,提高開發效率呢?
類型轉換
在 C# 中 所有的類型都直接或間接的繼承自 System.Object 類。換句話說,所有的類型都可以轉換為 Object 類。這為我們前面的問題提供了一個解決方案——實現一個以 Object 為類型參數的 Swap 函數。其實現如下:
?
void Swap(ref object lhs, ref object rhs)
{
??? object temp = lhs;
??? lhs = rhs;
??? rhs = temp;
}
?
調用的代碼如下:
//a, b 為要傳入 Swap 函數的變量
object objA = a;
object objB = b;
?
Swap(ref objA, ref objB);
?
//T 為變量 a 和 b 的數據類型
a = (T)objA;
b = (T)objB;
?
這一實現利用類型轉換有效的重用了 Swap 的代碼,但有兩點不足。
?
首先是性能問題。每次調用 Swap 函數前,需要對其參數進行一次向上的轉型;調用完之后,又要對其進行一次向下的轉型。如果需要多次調用 Swap 函數(比如在一個很大的循環中),轉型帶來的開銷是想當可觀的,特別是當參數為值類型的時候。
?
第二是,無法提供編譯時類型檢查。下面的例子雖然能通過編譯,但運行時會出現異常:
?
string a? = “This is a string”;
int b = 0;
?
object objA = a;
object objB = b;
?
Swap(ref objA, ref objB);?????????????? //可以編譯
?
a = (string)objA;?????????????? //出現運行時異常
b = (ing)objB;
?
針對以上兩點不足,C# 2.0 提出了泛型。
泛型
泛型是C# 2.0 提供的延遲類和函數中數據類型的定義,直到客戶代碼聲明或實例化該數據類型。
?
泛型版的 Swap 函數實現如下
?
void Swap<T>(ref T lhs, ref T rhs)
{
??? T temp = lhs;
??? lhs = rhs;
??? rhs = temp;
}??????? ?
?
泛型集合中的 <T>是obj類型
上例中的類型參數 T 可以實例化為任意數據類型。相對于通過類型轉換重用 Swap 函數,它且不需要類型轉換,有效的提高性能。而且,它還能提供編譯時類型檢查。調用語法與普通函數調用完全一樣。
泛型的優勢
從上面例子可以看出,使用泛型具有如下三點優勢:
?
??????? 避免重復代碼,最大化代碼重用
??????? 避免無謂的類型轉換,提高性能
??????? 提供編譯時類型檢查,具有類型安全性
C# code
// 在三角符號里寫入類型參數T
public class GenericList<T>
{
// Node為非泛型類,作為GenericList<T>的嵌套類
private class Node
{
// 在非泛型構造函數中使用T
public Node(T t)
{
next = null;
data = t;
}
private Node next;
public Node Next
{
get { return next; }
set { next = value; }
}
// T作為私有成員的數據類型
private T data;
// T作為屬性的返回類型
public T Data
{
get { return data; }
set { data = value; }
}
}
private Node head;
// 構造函數
public GenericList()
{
head = null;
}
// T 作為方法的參數類型
public void AddHead(T t)
{
Node n = new Node(t);
n.Next = head;
head = n;
}
public IEnumerator<T> GetEnumerator()
{
Node current = head;
while (current != null)
{
yield return current.Data;
current = current.Next;
}
}
}
下面的代碼示例演示客戶端代碼如何使用泛型 GenericList <T> 類來創建整數列表。只需更改類型參數,即可方便地修改下面的代碼示例,創建字符串或任何其他自定義類型的列表:
C# code
class TestGenericList
{
static void Main()
{
// int 是類型變量
GenericList<int> list = new GenericList<int>();
for (int x = 0; x < 10; x++)
{
list.AddHead(x);
}
foreach (int i in list)
{
System.Console.Write(i + " ");
}
System.Console.WriteLine("\n完成");
}
}
泛型也在用在類里,可以對參數進行約束而對于new約束而言有點特殊
{
????public?void?Add(K?key,?V?value)
????{
????????
????????if?(key.CompareTo(x)?<?0)?{}
????????
????}
}
這樣就保證了任何為K類型參數提供的類型都實現了IComparable接口。所以我們的key就可以使用CompareTo方法了。
如果我們在使用時提供了沒有實現IComparable接口的類型,就會出現編譯時錯誤。
對于new()約束,大家可能有一個誤解,以為使用了new約束之后,在創建對象時與非泛型的版本是一致的:publicclassTester<T>
whereT:new()
{
publicTester()
{
t=newT();//等同于非泛型版本的new?例如objecto=newobject();?
}
privateTt;
}
事實上,使用new關鍵字的作用只是讓編譯器在泛型實例化之處,檢查所綁定的泛型參數是否具有無參構造函數:
Tester<SomeType>t=newTester<SomeType>();
//此處編譯器會檢查SomeType是否具有無參構造函數。若沒有則會有compileerror
?
轉載于:https://www.cnblogs.com/dewin/archive/2009/06/28/1512868.html
總結
- 上一篇: COM, COM+ and .NET 的
- 下一篇: BSTR、char* 和 CString