四、類型參數的約束
若要檢查表中的一個元素,以確定它是否合法或是否可以與其他元素相比較,那麼編譯器必須保證:客戶代碼中可能出現的所有類型參數,都要支持所需調用的操作或方法。這種保證是通過在泛型類的定義中,應用一個或多個約束而得到的。一個約束類型是一種基類約束,它通知編譯器,只有這個類型的對象或從這個類型派生的對象,可被用作類型參數。一旦編譯器得到這樣的保證,它就允許在泛型類中調用這個類型的方法。上下文關鍵字where用以實現約束。下面的示例代碼說明了應用基類約束,爲MyList類增加功能。
public class Employee
{
public class Employee
{
private string name;
private int id;
public Employee(string s, int i)
{
name = s;
id = i;
}
public string Name
{
get { return name; }
set { name = value; }
}
public int ID
{
get { return id; }
set { id = value; }
}
}
}
class MyList<T> where T: Employee
{
//Rest of class as before.
public T FindFirstOccurrence(string s)
{
T t = null;
Reset();
while (HasItems())
{
if (current != null)
{
//The constraint enables this:
if (current.Data.Name == s)
{
t = current.Data;
break;
}
else
{
current = current.Next;
}
} //end if
} // end while
return t;
}
}
約束使得泛型類能夠使用Employee.Name屬性,因爲所有爲類型T的元素,都是一個Employee對象或是一個繼承自Employee的對象。
同一個類型參數可應用多個約束。約束自身也可以是泛型類,如下:
class MyList where T: Employee, IEmployee, IComparable, new()
{…}
類型參數的約束,增加了可調用的操作和方法的數量。這些操作和方法受約束類型及其派生層次中的類型的支持。因此,設計泛型類或方法時,如果對泛型成員執行任何賦值以外的操作,或者是調用System.Object中所沒有的方法,就需要在類型參數上使用約束。
無限制類型參數的一般用法
沒有約束的類型參數,如公有類MyClass{…}中的T, 被稱爲無限制類型參數(unbounded type parameters)。無限制類型參數有以下規則:
l 不能使用運算符 != 和 == ,因爲無法保證具體的類型參數能夠支持這些運算符。
l 它們可以與System.Object相互轉換,也可顯式地轉換成任何接口類型。
l 可以與null比較。如果一個無限制類型參數與null比較,當此類型參數爲值類型時,比較的結果總爲false。
無類型約束
當約束是一個泛型類型參數時,它就叫無類型約束(Naked type constraints)。當一個有類型參數成員方法,要把它的參數約束爲其所在類的類型參數時,無類型約束很有用。如下例所示:
class List
{
//…
void Add(List items) where U:T {…}
}
在上面的示例中, Add方法的上下文中的T,就是一個無類型約束;而List類的上下文中的T,則是一個無限制類型參數。
無類型約束也可以用在泛型類的定義中。注意,無類型約束一定也要和其它類型參數一起在尖括號中聲明:
//naked type constraint
public class MyClass
//創建一個類
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections;
namespace WindowsFormsApplication1
{
public class Class1<T> where T:int
{
T a;
public T Add(T b)
{
return b;
}
}
}
//主程式
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Collections;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
try
{
Class1<string> ss = new Class1<string>();
ss.Add("123");
}
catch(Exception ex)
{
}
}
}
}
注意這種寫法會導致錯誤,以爲加了約束條件。報錯如下:
由於加了約束條件會導致,這種錯誤因爲類型不一致。