C# 泛型类型参数的约束
生活随笔
收集整理的這篇文章主要介紹了
C# 泛型类型参数的约束
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
C# 泛型類型參數的約束
http://www.cnblogs.com/rinack/p/5676311.html在定義泛型類時,可以對客戶端代碼能夠在實例化類時用于類型參數的類型種類施加限制。如果客戶端代碼嘗試使用某個約束所不允許的類型來實例化類,則會產生編譯時錯誤。這些限制稱為約束。約束是使用 where 上下文關鍵字指定的。下表列出了六種類型的約束:
where T: struct
類型參數必須是值類型。可以指定除 Nullable 以外的任何值類型。有關更多信息,請參見使用可以為 null 的類型(C# 編程指南)。
where T : class
類型參數必須是引用類型;這一點也適用于任何類、接口、委托或數組類型。
where T:new()
類型參數必須具有無參數的公共構造函數。當與其他約束一起使用時,new() 約束必須最后指定。
where T:<基類名>
類型參數必須是指定的基類或派生自指定的基類。
where T:<接口名稱>
類型參數必須是指定的接口或實現指定的接口。可以指定多個接口約束。約束接口也可以是泛型的。
where T:U
為 T 提供的類型參數必須是為 U 提供的參數或派生自為 U 提供的參數。
使用約束的原因 ?
?
如果要檢查泛型列表中的某個項以確定它是否有效,或者將它與其他某個項進行比較,則編譯器必須在一定程度上保證它需要調用的運算符或方法將受到客戶端代碼可能指定的任何類型參數的支持。這種保證是通過對泛型類定義應用一個或多個約束獲得的。例如,基類約束告訴編譯器:僅此類型的對象或從此類型派生的對象才可用作類型參數。一旦編譯器有了這個保證,它就能夠允許在泛型類中調用該類型的方法。約束是使用上下文關鍵字 where 應用的。
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; }} }public class GenericList<T> where T : Employee {private class Node{private Node next;private T data;public Node(T t){next = null;data = t;}public Node Next{get { return next; }set { next = value; }}public T Data{get { return data; }set { data = value; }}}private Node head;public GenericList() //constructor{head = null;}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;}}public T FindFirstOccurrence(string s){Node current = head;T t = null;while (current != null){//The constraint enables access to the Name property.if (current.Data.Name == s){t = current.Data;break;}else{current = current.Next;}}return t;} }
約束使得泛型類能夠使用 Employee.Name 屬性,因為類型為 T 的所有項都保證是 Employee 對象或從 Employee 繼承的對象。
可以對同一類型參數應用多個約束,并且約束自身可以是泛型類型,如下所示:
class EmployeeList<T> where T : Employee, IEmployee, System.IComparable<T>, new()
{
? ? // ...
}
?
通過約束類型參數,可以增加約束類型及其繼承層次結構中的所有類型所支持的允許操作和方法調用的數量。因此,在設計泛型類或方法時,如果要對泛型成員執行除簡單賦值之外的任何操作或調用 System.Object 不支持的任何方法,您將需要對該類型參數應用約束。
在應用 where T : class 約束時,避免對類型參數使用 == 和 != 運算符,因為這些運算符僅測試引用同一性而不測試值相等性。即使在用作參數的類型中重載這些運算符也是如此。下面的代碼說明了這一點;即使 String 類重載 == 運算符,輸出也為 false。
public static void OpTest<T>(T s, T t) where T : class {System.Console.WriteLine(s == t); } static void Main() {string s1 = "target";System.Text.StringBuilder sb = new System.Text.StringBuilder("target");string s2 = sb.ToString();OpTest<string>(s1, s2); }
這種情況的原因在于,編譯器在編譯時僅知道 T 是引用類型,因此必須使用對所有引用類型都有效的默認運算符。如果必須測試值相等性,建議的方法是同時應用 where T : IComparable<T> 約束,并在將用于構造泛型類的任何類中實現該接口。
約束多個參數
可以對多個參數應用約束,并對一個參數應用多個約束,如下面的示例所示:
class Base { }
class Test<T, U>
? ? where U : struct
? ? where T : Base, new() { }
沒有約束的類型參數(如公共類 SampleClass<T>{} 中的 T)稱為未綁定的類型參數。未綁定的類型參數具有以下規則:
不能使用 != 和 == 運算符,因為無法保證具體類型參數能支持這些運算符。
可以在它們與 System.Object 之間來回轉換,或將它們顯式轉換為任何接口類型。
可以將它們與 null 進行比較。將未綁定的參數與 null 進行比較時,如果類型參數為值類型,則該比較將始終返回 false。
作為約束的類型參數
將泛型類型參數作為約束使用,在具有自己類型參數的成員函數必須將該參數約束為包含類型的類型參數時非常有用,如下示例所示:
class List<T>
{
? ? void Add<U>(List<U> items) where U : T {/*...*/}
}
類型參數還可在泛型類定義中用作約束。請注意,必須在尖括號中聲明此類型參數與任何其他類型的參數:
//Type parameter V is used as a type constraint.
public class SampleClass<T, U, V> where T : V { }
泛型類的類型參數約束的作用非常有限,因為編譯器除了假設類型參數派生自 System.Object 以外,不會做其他任何假設。在希望強制兩個類型參數之間的繼承關系的情況下,可對泛型類使用參數類型約束。
========
泛型類型參數及約束
http://www.cnblogs.com/wanghonghu/archive/2012/07/11/2586920.html泛型類型參數簡介
? ? 在定義泛型類型和泛型方法時,常用到泛型類型參數,泛型類型參數是在實例化泛型時指定類型的占位符。泛型類型參數放在“<>”內。
? ? 泛型類型參數命名建議:
? ? (1)當泛型類型參數為單個字母時,建議用T表示。
? ? (1)當泛型類型參數用單詞定義時,建議在單詞前加T。
private void PromptName<T>(T t) {}
private void PromptName<Tuser>(Tuser user){}
泛型類型參數約束
? ? ? 在定義泛型類時,可以對在實例化泛型類時用于類型參數的類型種類施加限制。如果實例化泛型類時使用某個約束所不允許的類型來實例化類,則會產生編譯時錯誤。
? ? ? 泛型約束分類: ?
約束
說明
T:結構
類型參數必須是值類型。 可以指定除 Nullable 以外的任何值類型。
T:類
類型參數必須是引用類型;這一點也適用于任何類、接口、委托或數組類型。
T:new()
類型參數必須具有無參數的公共構造函數。 當與其他約束一起使用時,new() 約束必須最后指定。
T:<基類名>
類型參數必須是指定的基類或派生自指定的基類。
T:<接口名稱>
類型參數必須是指定的接口或實現指定的接口。 可以指定多個接口約束。 約束接口也可以是泛型的。
T:U
為 T 提供的類型參數必須是為 U 提供的參數或派生自為 U 提供的參數。
?(1)類型參數約束為結構(struct)。
public class ShowObjectType<T> where T : struct{public void ShowValue<T>(T t){Console.WriteLine(t.GetType());}}class GenericConstraint{static void Main(){ ShowObjectType<int> showInt = new ShowObjectType<int>();showInt.ShowValue<int>(5);showInt.ShowValue(5);//從參數可以推導出類型參數類型,則可以省略類型參數類型//因為約束為值類型,下面代碼不能通過編譯ShowObjectType<string> showString = new ShowObjectType<string>();showString.ShowValue("5");Console.Read();}}
? ?(2)類型參數約束為類(class)。
? ? 在應用 where T : class 約束時,避免對類型參數使用 == 和 != 運算符,因為這些運算符僅測試類型為引用類型,而不測試值相等性。
class GenericConstraint{static void Main(){List<string > list = new List<string>();AddClass<string>(list, "hello generic");Console.Read();}private static void AddClass<T>(List<T> list, T t) where T : class{list.Add(t);}}
? (4)類型參數約束為具體類。
? ? ? ? ?約束為具體類時,可利用類型參數調用具體類的屬性和方法。
? ? class GenericConstraint
? ? {
? ? ? ? static void Main()
? ? ? ? {
? ? ? ? ? ? Person person = new Person { ID = 1, Name = "David" };
? ? ? ? ? ? PromptName<Person>(person);
? ? ? ? ? ? Console.Read();
? ? ? ? }
? ? ? ? //此約束T為Person對象或者繼承Person對象
? ? ? ? private static void PromptName<T>(T t) where T : Person?
? ? ? ? {
? ? ? ? ? ? //此處可使用Person的Name屬性
? ? ? ? ? ? if (t.Name == "David")
? ? ? ? ? ? {
? ? ? ? ? ? ? ? Console.WriteLine("Person name is David");
? ? ? ? ? ? }
? ? ? ? ? ? string name = t.GetName();
? ? ? ? ? ? Console.WriteLine("Person name is {0}", name);
? ? ? ? }
? ? }
? ? public class Person
? ? {
? ? ? ? private int id;
? ? ? ? public int ID
? ? ? ? {
? ? ? ? ? ? get { return id; }
? ? ? ? ? ? set { id = value; }
? ? ? ? }
? ? ? ? private string name;
? ? ? ? public string Name
? ? ? ? {
? ? ? ? ? ? get { return name; }
? ? ? ? ? ? set { name = value; }
? ? ? ? }
? ? ? ? public string GetName()
? ? ? ? {
? ? ? ? ? ? return Name;
? ? ? ? }
? ? }
復制代碼
? ? (5)約束多個參數。
class Base { }
class Test<T, U>
? ? where U : struct
? ? where T : Base, new() { }
? ? (6)未綁定類型參數。
? ? ? 沒有約束的類型參數,稱為未綁定的類型參數。
class ?List<T>{}
========
泛型類型參數約束 <T> where T : class,new(){}
.NET支持的類型參數約束有以下五種:
where T : struct ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? | T必須是一個結構類型
where T : class ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? | T必須是一個類(class)類型,不能是結構(structure)類型 ? 引用類型
where T : new() ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? | T必須要有一個無參構造函數 ? ?可以被new()
where T : NameOfBaseClass ? ? ? ? ?| T必須繼承名為NameOfBaseClass的類
where T : NameOfInterface ? ? ? ? ? ? | T必須實現名為NameOfInterface的接口
========
where(泛型類型約束)
http://www.cnblogs.com/lystory/p/5104865.html定義:在定義泛型的時候,我們可以使用 where 限制參數的范圍。
使用:在使用泛型的時候,你必須尊守 where 限制參數的范圍,否則編譯不會通過。
六種類型的約束:
T:類(類型參數必須是引用類型;這一點也適用于任何類、接口、委托或數組類型。)
? ? class MyClass<T, U>
? ? ? ? where T : class///約束T參數必須為“引用 類型{ }”
? ? ? ? where U : struct///約束U參數必須為“值 類型”
? ? { }
T:結構(類型參數必須是值類型。可以指定除 Nullable 以外的任何值類型。)
? ? class MyClass<T, U>
? ? ? ? where T : class///約束T參數必須為“引用 類型{ }”
? ? ? ? where U : struct///約束U參數必須為“值 類型”
? ? { }
T:new()(類型參數必須具有無參數的公共構造函數。當與其他約束一起使用時,new() 約束必須最后指定。)
class EmployeeList<T> where T : Employee, IEmployee, System.IComparable<T>, new()
{
? ? // ...
}
T:<基類名>(類型參數必須是指定的基類或派生自指定的基類。)
public class Employee{}
public class GenericList<T> where T : Employee
T:<接口名稱>(類型參數必須是指定的接口或實現指定的接口。可以指定多個接口約束。約束接口也可以是泛型的。)
復制代碼
? ? /// <summary>
? ? /// 接口
? ? /// </summary>
? ? interface IMyInterface
? ? {
? ? }
? ? /// <summary>
? ? /// 定義的一個字典類型
? ? /// </summary>
? ? /// <typeparam name="TKey"></typeparam>
? ? /// <typeparam name="TVal"></typeparam>
? ? class Dictionary<TKey, TVal>
? ? ? ? where TKey : IComparable, IEnumerable
? ? ? ? where TVal : IMyInterface
? ? {
? ? ? ? public void Add(TKey key, TVal val)
? ? ? ? {
? ? ? ? }
? ? }
復制代碼
T:U(為 T 提供的類型參數必須是為 U 提供的參數或派生自為 U 提供的參數。也就是說T和U的參數必須一樣)
class List<T>
{
? ? void Add<U>(List<U> items) where U : T {/*...*/}
}
?
一、可用于類:
public class MyGenericClass<T> where T:IComparable { }
二、可用于方法:
public bool MyMethod<T>(T t) where T : IMyInterface { }
三、可用于委托:
delegate T MyDelegate<T>() where T : new()
?
在實際項目中什么時候用到它們?
有時候你在做一個項目的時候,你需要用到泛型,你只希望傳給你的泛型參數是限定范圍的,
比如你希望值類型,或者是引用類型,或者是繼承至某個類型、或者是符合某個接扣的類型,
這個時候你該如何辦?你就需要用到 WHERE 來限定了。
?
參考文檔:
https://msdn.microsoft.com/zh-cn/library/d5x73970.aspx
https://msdn.microsoft.com/zh-cn/library/bb384067.aspx
========
c# 泛型類型參數與約束的深入分析
http://www.jb51.net/article/37657.htm本篇文章是對c#中泛型類型參數與約束進行了詳細的分析介紹,需要的朋友參考下
..泛型類型參數簡介
在定義泛型類型和泛型方法時,常用到泛型類型參數,泛型類型參數是在實例化泛型時指定類型的占位符。泛型類型參數放在“<>”內。
泛型類型參數命名建議:
(1)當泛型類型參數為單個字母時,建議用T表示。
(2)當泛型類型參數用單詞定義時,建議在單詞前加T。
復制代碼 代碼如下:
private void PromptName<T>(T t) {}
private void PromptName<Tuser>(Tuser user){}
泛型類型參數約束
在定義泛型類時,可以對在實例化泛型類時用于類型參數的類型種類施加限制。如果實例化泛型類時使用某個約束所不允許的類型來實例化類,則會產生編譯時錯誤。
泛型約束分類: ?
約束
?說明
?
T:結構
?類型參數必須是值類型。 可以指定除 Nullable 以外的任何值類型。?
?
T:類
?類型參數必須是引用類型;這一點也適用于任何類、接口、委托或數組類型。
?
T:new()
?類型參數必須具有無參數的公共構造函數。 當與其他約束一起使用時,new() 約束必須最后指定。
?
T:<基類名>
?類型參數必須是指定的基類或派生自指定的基類。
?
T:<接口名稱>
?類型參數必須是指定的接口或實現指定的接口。 可以指定多個接口約束。 約束接口也可以是泛型的。
?
T:U
?為 T 提供的類型參數必須是為 U 提供的參數或派生自為 U 提供的參數。
?
(1)類型參數約束為結構(struct)。
復制代碼 代碼如下:
public class ShowObjectType<T> where T : struct
? ? {
? ? ? ? public void ShowValue<T>(T t)
? ? ? ? {
? ? ? ? ? ? Console.WriteLine(t.GetType());
? ? ? ? }
? ? }
? ? class GenericConstraint
? ? {
? ? ? ? static void Main()
? ? ? ? {
? ? ? ? ? ? ShowObjectType<int> showInt = new ShowObjectType<int>();
? ? ? ? ? ? showInt.ShowValue<int>(5);
? ? ? ? ? ? showInt.ShowValue(5);//從參數可以推導出類型參數類型,則可以省略類型參數類型
? ? ? ? ? ? //因為約束為值類型,下面代碼不能通過編譯
? ? ? ? ? ? ShowObjectType<string> showString = new ShowObjectType<string>();
? ? ? ? ? ? showString.ShowValue("5");
? ? ? ? ? ? Console.Read();
? ? ? ? }
? ? }
(2)類型參數約束為類(class)。
在應用 where T : class 約束時,避免對類型參數使用 == 和 != 運算符,因為這些運算符僅測試類型為引用類型,而不測試值相等性。
復制代碼 代碼如下:
class GenericConstraint
? ? {
? ? ? ? static void Main()
? ? ? ? {
? ? ? ? ? ? ? List<string > list = new List<string>();
? ? ? ? ? ? AddClass<string>(list, "hello generic");
? ? ? ? ? ? Console.Read();
? ? ? ? }
? ? ? ? private static void AddClass<T>(List<T> list, T t) where T : class
? ? ? ? {
? ? ? ? ? ? list.Add(t);
? ? ? ? }
? ? }
(3)類型參數約束為具體類。
約束為具體類時,可利用類型參數調用具體類的屬性和方法。
復制代碼 代碼如下:
class GenericConstraint
? ? {
? ? ? ? static void Main()
? ? ? ? {
? ? ? ? ? ? Person person = new Person { ID = 1, Name = "David" };
? ? ? ? ? ? PromptName<Person>(person);
? ? ? ? ? ? Console.Read();
? ? ? ? }
? ? ? ? //此約束T為Person對象或者繼承Person對象
? ? ? ? private static void PromptName<T>(T t) where T : Person?
? ? ? ? {
? ? ? ? ? ? //此處可使用Person的Name屬性
? ? ? ? ? ? if (t.Name == "David")
? ? ? ? ? ? {
? ? ? ? ? ? ? ? Console.WriteLine("Person name is David");
? ? ? ? ? ? }
? ? ? ? ? ? string name = t.GetName();
? ? ? ? ? ? Console.WriteLine("Person name is {0}", name);
? ? ? ? }
? ? }
? ? public class Person
? ? {
? ? ? ? private int id;
? ? ? ? public int ID
? ? ? ? {
? ? ? ? ? ? get { return id; }
? ? ? ? ? ? set { id = value; }
? ? ? ? }
? ? ? ? private string name;
? ? ? ? public string Name
? ? ? ? {
? ? ? ? ? ? get { return name; }
? ? ? ? ? ? set { name = value; }
? ? ? ? }
? ? ? ? public string GetName()
? ? ? ? {
? ? ? ? ? ? return Name;
? ? ? ? }
? ? }
(4)約束多個參數。
復制代碼 代碼如下:
class Base { }
class Test<T, U>
? ? where U : struct
? ? where T : Base, new() { }
(5)未綁定類型參數。
?沒有約束的類型參數,稱為未綁定的類型參數。
復制代碼 代碼如下:
class ?List<T>{}
========
總結
以上是生活随笔為你收集整理的C# 泛型类型参数的约束的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C#指针使用学习总结
- 下一篇: Spring.NET 学习总结