where關鍵詞一個最重要的用法就是在泛型的聲明、定義中作出約束。
約束又分爲接口約束、基類約束、構造函數約束、函數方法的約束,咱們慢慢介紹。markdown
顧名思義,泛型參數必須實現相應的接口才能夠,看一個例子:函數
public interface IAccount {
string Name {
get;
}
decimal Balance {
get;
}
}
public class Account : IAccount {
private string name;
public string Name {
get {
return name;
}
}
private decimal balance;
public decimal Balance {
get {
return balance;
}
}
public Account(string name = "", decimal balance = 0) {
this.name = name;
this.balance = balance;
}
}
public class MyClass<T> where T : IAccount {
public MyClass() {
Console.WriteLine("In MyClass<T> Ctor");
}
}
在public class MyClass<T> where T : IAccount
中,where關鍵詞指定了T必須實現IAcoount的接口才能夠成功構造,例如:this
namespace CSharp {
class Program {
static void Main(string[] args) {
MyClass<Account> mc = new MyClass<Account>();
//成功,Account實現了IAccount接口
MyClass<string> m = new MyClass<string>();
//構造失敗,string沒有實現IAccount接口,編譯器提示錯誤
}
}
}
T也能夠是泛型接口,例如MSDN給出的例子:spa
public class MyGenericClass<T> where T:IComparable { }
類型參數必須是指定的基類或派生自指定的基類,多用於繼承體系之下,看個例子:.net
public class Account : IAccount {
private string name;
public string Name {
get {
return name;
}
}
private decimal balance;
public decimal Balance {
get {
return balance;
}
}
public Account(string name = "", decimal balance = 0) {
this.name = name;
this.balance = balance;
}
}
public class AccountDrived : Account {
public AccountDrived(string name = "", decimal balance = 0):base(name, balance) {
Console.WriteLine("In AccountDrived Ctor");
}
}
//泛型參數只能是Account或者Account的派生類
public class MyClass2<T> where T : Account {
public MyClass2() {
Console.WriteLine("In MyClass2<T> Ctor");
}
}
class Program {
static void Main(string[] args) {
MyClass2<Account> a = new MyClass2<Account>();
MyClass2<AccountDrived> b = new MyClass2<AccountDrived>();
//MyClass2<string> c = new MyClass2<string>(); - error
}
}
顧名思義,對類的構造函數進行了必定的約束,舉個例子:code
public class NoDefaultAccount : IAccount {
private string name;
public string Name {
get {
return name;
}
}
private decimal balance;
public decimal Balance {
get {
return balance;
}
}
public NoDefaultAccount(string name) {
this.name = name;
this.balance = 0;
}
}
public class Account : IAccount {
private string name;
public string Name {
get {
return name;
}
}
private decimal balance;
public decimal Balance {
get {
return balance;
}
}
public Account(string name = "", decimal balance = 0) {
this.name = name;
this.balance = balance;
}
}
public class AccountDrived : Account {
}
public class MyClass3<T> where T : class, new(){
public MyClass3(){
Console.WriteLine("In MyClass3<T> Ctor");
}
}
class Program {
static void Main(string[] args) {
//1.MyClass3<Account> a = new MyClass3<Account>();
MyClass3<AccountDrived> b = new MyClass3<AccountDrived>();//默認生成一個無參構造函數
//2.MyClass3<NoDefaultAccount> c = new MyClass3<NoDefaultAccount>();//必須是有默認構造函數的非抽象類
}
}
這裏的重點是public class MyClass3<T> where T : class, new()
,這代表參數T對應的類型必須是一個引用類型(class),new()表示具有無參構造函數。對象
NoDefaultAccount類內顯然沒有默認的構造函數,在Account中有public Account(string name = "", decimal balance = 0)
,給定了默認值,在AccountDerived中,因爲咱們沒有顯式的聲明一個構造函數,因而C#會自動生成一個AccountDerived()。blog
使人疑惑的是,Account是有默認構造函數的,爲什麼//1.MyClass3<Account> a = new MyClass3<Account>();
這條語句編譯器會報錯呢?
嘗試後發現,C#和C++不同,當你寫下Account a = new Account();
這條語句的時候,編譯器會優先查找是否有public Account()
,若是存在那麼就構造對象,不然查找public Account(value = defaultvalue)
這種帶默認值的構造函數,二者是不同的,而且是能夠共存的。繼承
class Account{
//和C++不一樣,這並非重定義
public Account() {
this.name = "xxxxx";
this.balance = 10;
}
public Account(string name = "", decimal balance = 0) {
this.name = name;
this.balance = balance;
}
}
new()這種約束特指是否存在 Account()這樣的無參默認構造函數。接口
這種形式就比較簡單了,上述三個約束不加在泛型類中,加在函數中便可,舉個例子:
public class Algorithm {
public static decimal Total<TAccount>(IEnumerable<TAccount> e)
where TAccount : IAccount
//這意味着調用Total函數傳入的參數e必須是1.實現了IEnumerable接口的可迭代對象 2.e的可迭代元素必須是實現了IAcoount接口的
{
decimal total = 0;
foreach(TAccount element in e) {
total += element.Balance;
}
return total;
}
public static void Add<T>(T lhs, T rhs) where T : class, new() {
//約束了T必須是引用類型,且必須定義了默認構造函數
T ans = new T();
}
}
class Program {
static void Main(string[] args) {
List<Account> accounts = new List<Account>();
accounts.Add(new Account("sixday", 100));
accounts.Add(new Account("fiveday", 50));
accounts.Add(new Account("sevenday", 70));
Console.WriteLine("The answer is {0}", Algorithm.Total<Account>(accounts));
}
}
最後,作一個小總結:
更多例子能夠參考MSDN
除了用於泛型約束以外,where還經常使用於查詢表達式,能夠直接參考MSDN的例子。
出處:https://blog.csdn.net/sixdaycoder/article/details/75356055