(轉)C# Where關鍵詞的用法


where(泛型類型約束)

where關鍵詞一個最重要的用法就是在泛型的聲明、定義中做出約束。 
約束又分為接口約束、基類約束、構造函數約束、函數方法的約束,我們慢慢介紹。

接口約束

顧名思義,泛型參數必須實現相應的接口才可以,看一個例子:

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的接口才可以成功構造,例如:

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給出的例子:

public class MyGenericClass<T> where T:IComparable { }  

基類約束

類型參數必須是指定的基類或派生自指定的基類,多用於繼承體系之下,看個例子:

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 } }

構造函數約束

顧名思義,對類的構造函數進行了一定的約束,舉個例子:

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()。

令人疑惑的是,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)); } }

泛型類型約束總結

最后,做一個小總結:

  • where T : struct 這表明T必須是一個值類型,像是int,decimal這樣的
  • where T : class 這表明T必須是一個引用類型,像是自定義的類、接口、委托等
  • where T : new() 這表明T必須有無參構造函數,且如果有多個where約束,new()放在最后面
  • where T : [base class name] 這表明T必須是base class類獲其派生類
  • where T : [interface name] 這表明T必須實現了相應的接口

更多例子可以參考MSDN

where (查詢表達式)

除了用於泛型約束之外,where還常用於查詢表達式,可以直接參考MSDN的例子

文章轉載自:https://blog.csdn.net/sixdaycoder/article/details/75356055


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM