泛型的優勢

來源:互聯網
上載者:User
 

假設需要一個兩個整形變數交換的函數,我們很快就可以嗒嗒嗒嗒的敲出下面的 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 class Dictionary<K,V> where K: IComparable
{
    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

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.