1. 程式人生 > >C#中的泛型與C++中的模板

C#中的泛型與C++中的模板

今天一個同事問我C#中有沒有模板函式,他想寫一個函式能夠處理不同的型別,裡面演算法一樣,如果要過載實現的話,就造成大量重複的程式碼,而且擴充套件性不好。我說肯定有啊,你可以上網搜一下,結果他說沒搜到,而我對C#根本就不熟,我就和他說你先按照過載的方式先做著,我再查查。

晚上回家之後,拿了本C#的書翻了一下,一下就找到了,就是C#泛型,我猜同事肯定搜的”模板“,所以沒搜到。下面記錄一下C#的泛型和C++中的模板不同之處:

1,宣告方式

C#中的泛型宣告方式為,在識別符號後面直接加<T>即可,而C++需要使用template關鍵字,如下:

C#:

class Stack<T>

{

public void Push(T name);

......

};

C++:

template <class T>

class Stack

{

public:

void Push(T name);

};

從上面的兩種寫法可以看出,顯然C#要更簡單一些。

上面這是類的泛型化,對於函式的泛型,也類似,如下:

C#:

void Func<T>(T name);

C++:

template <class T>

void Func(T name);

2,泛型約束

C#的泛型比C++模板增加了一個泛型引數約束功能,這在某些場景下非常有用,它可以限制某個泛型引數的使用範圍,例如我們可以約束泛型型別Shop<T>的引數只能為Customer類或其子類。如果我們這麼寫:

class Customer
{
public string Name{get;set;};
public string CreditCardNo{get;set;};
}
class Shop<T>
{
public void Print(T customer)
{
var cust = (Customer)customer;
Console.WriteLine(cust.Name);
............
}
}

那麼var cust = (Customer)customer;這句話將編譯錯誤,C#無法進行型別轉換,因為對於型別引數T,C#在編譯期間根本知道它是什麼型別,所以無法進行轉換,但是如果對T做個限制,結果會怎麼樣呢,先看看C#中泛型約束的語法:

class Shop<T> where T: Customer

很簡單,就是增加一個where子句,約束的型別如果有多個可以用逗號隔開,如下:

class Shop<T> where T: class, ICustomer, new()

如果型別引數有多個,則寫多個where,如下:

class Shop<T1, T2> where T1:Customer where T2:Superman

這裡要注意的是C#的泛型約束是有很多要求的,約束型別只有6種,如下:

class:型別實參必須是引用型別;

struct:型別實參必須是值型別;

<base class name>:型別實參必須是指定的類或者其派生類;

<interface name>:型別實參必須是指定的介面類,或其派生類

new():型別實參必須有一個無引數的公共建構函式;

U:型別實參T必須是另一個型別實參U或者U的派生類。

而且where子句內部的約束型別是有順序要求的,首先class,struct,<base class name>必須只能取其中一個,且只能放在最前面,之後是<interface name>,這個可以是多個,而new()必須放在最後,向int,string這些 密封類則不能作為約束引數的。

所以上面的語句如果寫成如下,就不會有錯了,而且VS的IntelliSense會非常智慧地彈出customer的成員,如下:

class Customer
{
public string Name{get;set;};
public string CreditCardNo{get;set;};
}
class Shop<T>
{
public void Print(T customer)
{
Console.WriteLine(customer.Name);
}
}

其他關於C++的模板和C#的泛型使用上基本一樣,除了C#特有的一些功能,如委託泛型,介面泛型等。

對了,其實我同事那個問題用object做函式的引數也是可以實現的,但是執行效率比泛型要低。