接泛型二
20.4 泛型委派聲明
委託聲明可以包含型別參數。
delegate-declaration:
attributes opt delegate-modifiers op t delegate return-type identifier type-parameter-list opt
(formal-parameter-list opt) type-parameter-constraints-clauses opt;
(委託聲明: 特性可選 委託修飾符可選 delegate 傳回型別 標識符 型別參數列表可選 (正式參數列表可選 )型別參數約束語句可選
使用型別參數聲明的委託是一個泛型委派聲明。委託聲明只有在支援型別參數列表時,才能支援型別參數約束語句(§20.7)。除了所指出的之外,泛型委派聲明和常規的委託聲明遵循相同的規則。泛型委派聲明中的每個型別參數,在與委託關聯的特定聲明空間(§3.3)定義了一個名字。在委託聲明中的型別參數的範圍包括傳回型別、正式參數列表和型別參數約束語句。
像其他泛型型別聲明一樣,必須給定類型實參以形成構造委託類型。構造委託類型的參數和傳回值,由委託聲明中構造委託類型的每個型別參數對應的實參替代所形成。而結果傳回型別和參數類型用於確定什麼方法與構造委託類型相容。例如
delegate bool Predicate<T>(T value)class X{static bool F(int i){…}static bool G(string s){…}static void Main(){Predicate<int> p1 = F;Predicate<string> p2=G;}}
注意在先前的Main方法中的兩個賦值等價於下面的較長形式.
static void Main(){Predicate<int> p1 = new Predicate<int>(F);Predicate<string> p2 = new Predicate<string>(G);}
由於方法群組轉換,較短的形式也是可以的,這在§21.9中有說明。
20.5構造類型
泛型型別聲明自身並不表示一個類型。相反,泛型型別聲明通過應用類型實參的方式被用作形成許多不同類型的“藍圖”。型別參數被寫在角括弧之間,並緊隨泛型型別聲明名字之後。使用至少一個實參而被命名的類型被稱為構造類型(constructed type)。構造類型可以用在語言中類型名字可以出現的大多數地方。
type-name:(類型名字:)namespace-or-type-name(命名空間或類型名字)namespace-or-type-name:(命名空間或類型名字:)identifier type-argument-list(標識符類型實參列表可選)namespace-or-type-name. identifier(命名空間或類型名字.標識符)type-argument-list opt(類型實參列表可選)
構造類型也能被用在運算式中作為簡單名字(§20.9.3)或者訪問一個成員(§20.9.4)。
當一個命名空間或類型名字被計算時,只有帶有正確數量型別參數的泛型型別會被考慮。由此,只要類型有不同數量的型別參數並且聲明在不同的命名空間,那麼使用相同的標識符標識不同的類型是可能的。這對於在同一程式中混合使用泛型和非泛型類是很有用的。
namespace System.Collections{class Queue{…}}namespace Sysetm.Collections.Generic{class Queue<ElementType>{…}}namespace MyApplication{using System.Collections;using System.Collections.Generic;class X{Queue q1; //System.Collections.QueueQueue<int> q2;//System.Collections.Generic.Queue}}
在這些代碼中對於名字尋找的詳細規則在§20.9中進行了描述。在這些代碼中對於模糊性的決議在§20.6.5中進行了描述。
類型名字可能標識一個構造類型,儘管它沒有直接指定型別參數。這種情況在一個類型嵌套在一個泛型類聲明中時就會出現,並且包含聲明的執行個體類型將因為名字尋找(§20.1.2)而被隱式地使用。
class Outer<T>{public class Inner{…}public Inner i; //i的類型是Outer<T>.Inner}
在不安全的程式碼中,構造類型不能被用作非託管類型(§18.2)。
20.5.1類型實參
在一個型別參數列表中的每個實參都只是一個類型。
type-argument-list:(類型實參列表:)<type-arguments>(<類型實參>)type-arguments:(類型實參:)type-argument(類型實參)type-arguments, type-argument(類型實參,類型實參)type-argument:(類型實參:)type(類型)
類型實參反過來也可以是構造類型或型別參數。在不安全的程式碼中(§18),類型實參不能是指標類型。每個類型實參必須遵循對應型別參數(§20.7.1)上的任何約束。
20.5.2開放和封閉類型
所有類型都可以被分為開放類型(open type)或封閉類型(closed type)。開放類型是包含型別參數的類型。更明確的說法是
型別參數定義了一個開放類型
數群組類型只有當其元素是一個開放類型時才是開放類型
構造類型只有當其類型實參中的一個或多個是開放類型時,它才是開放類型
非開放類型都是封閉類型。
在運行時,在泛型型別聲明中的所有代碼都在一個封閉構造類型的上下文執行,這個封閉構造類型是通過將類型實參應用到泛型聲明中建立的。在泛型型別中的每個類型實參被綁定到一個特定運行時類型。所有語句和運算式的運行時處理總是針對封閉類型發生,而開放類型只發生在編譯時間處理。
每個封閉構造類型都有它自己的一組靜態變數,它們並不被其他封閉類型共用。因為在運行時不存在開放類型,所以開放類型沒有關聯的靜態變數。如果兩個封閉構造類型是從同一個型別宣告構造的,並且對應的類型實參也是相同的類型,那麼它們就是相同的類型。
20.5.3構造類型的基類和介面
構造類類型有一個直接基類,就像是一個簡單類類型。如果泛型類聲明沒有指定基類,其基類為object。如果基類在泛型類聲明中被指定,構造類型的基類通過將在基類聲明中的每個型別參數,替代為構造類型對應類型實參而得到。給定泛型類聲明
class B<U , V>{…}class G<T>:B<string , T[]>{…}
構造類型G<int>的基類將會是B<string , int[]>。
相似地,構造類、結構和介面類型有一組顯式的基底介面。顯式基底介面通過接受泛型型別聲明中的顯式基底介面聲明和某種替代而形成,這種替代是將在基底介面聲明中的每個型別參數,替代為構造類型的對應類型實參。
一個類型的所有基類和基底介面通過遞迴地得到中間基類和介面的基類與介面而形成。例如,給定泛型類聲明
class A {…}class B<T>:A{…}class C<T>:B<IComparable<T>>{…}class D<T>:C<T[]>{…}D<int>的基類是C<int[]>,B<IComparable<int[]>>,A和object。
20.5.4構造類型的成員
構造類型的非繼承成員通過替代成員聲明的類型實參,構造類型的對應類型實參而得到。
例如,給定泛型類聲明
class Gen<T,U>{public T[,],a;public void G(int i ,T t , Gen<U, T> gt){…}public U Prop(get{…}) set{…}}public int H{double d}{…}}
構造類型Gen<int[],IComparable<string>>有如下的成員。
public int[,][] a;public void G(int I , int[] t , Gen<IComparable<string>,int[] gt>){…}public IComparable<string> Prop{get{…} set{…}}public int H(double d){…}
注意替代處理是基於型別宣告的語義意義的,並不是簡單的基於文本的替代。在泛型類聲明Gen中的成員a的類型是“T的二維數組” 因此在先前執行個體化類型中的成員a的類型是“int型的一維數組的二維數組”或int[,][]。
構造類型的繼承成員以一種相似的方法得到。首先直接基類的所有成員是已經確定的。如果基類自身是構造類型這可能包括當前規則的遞迴應用。然後,繼承成員的每一個通過將成員聲明中的每個型別參數,替代為構造類型對應類型實參而被轉換。
class B<U>{public U F(long index){…}}class D<T>:B<T[]>{public T G(string s){…}}
在先前的例子中,構造類型D<int>的非繼承成員public int G(string s)通過替代型別參數T的類型實參int而得到。D<int>也有一個從類聲明B而來的繼承成員。這個繼承成員通過首先確定構造類型B<T[]>的成員而被確定,B<T[]>成員的確定是通過將U替換為替換為T[],產生public T[] F(long index)。然後類型實參int替換了型別參數T,產生繼承成員public int[] F(long index)。
20.5.5構造類型的可訪問性
當構造類型C<T1,…,TN>的所有部分C,T1,…,TN 可訪問時,那麼它就是可訪問的。例如,如果泛型型別名C是public,並且所有型別參數T1,…,TN也是public ,那麼構造類型的可訪問性也是public 。如果類型名或類型實參之一是private,那麼構造類型的可訪問性是private。如果類型實參之一可訪問性是protected,另一個是internal,那麼構造類型的可訪問性僅限於該類,以及本程式集之內的子類。
20.5.6轉換
構造類型遵循與非泛型型別相同的規則(§6)。當應用這些規則時,構造類型的基類和介面必須按§20.5.3中所描述的方式確定。
除了那些在§6中所描述的之外,構造參考型別之間不存在特別的轉換。尤其是,不像數群組類型,構造參考型別不允許“co-variant”轉換。也就是說,類型List<B>不能轉換到類型List<A>(無論是隱式或顯式)即使是B派生於A也是如此。同樣,也不存在從List<B>到List<object>的轉換。
對於這一點的基本原理是很簡單的:如果可以轉換到List<A>,很顯然你可以儲存一個類型A的值到這個list中。這將破壞在List<B>類型中的每個對象總是類型B的值這種不變性,或者當在集合類上賦值時,將出現不可預料的錯誤。
轉換的行為和運行時類型檢查示範如下。
class A {…}class B:A{…}class Colletion{…}class List<T>:Collection{…}class Test{void F(){List<A> listA = new List<A>();List<B> listB= new List<B>();Collection c1 = listA; //OK,List<A>是一個集合Collection c2 = listB; //OK,List<B>是一個集合List<A> a1 = listB; //錯誤,沒有隱式的轉換List<A> a2 = (List<A>)listB; //錯誤,沒有顯式的轉換}}
20.5.7System.Nullable<T>類型
在.NET基底類別庫中定義了泛型結構類型System.Nullable<T>泛型結構類型,它表示一個類型T的值可以為null。System.Nullable<T>類型在很多情形下是很有用的,例如用於指示資料庫表的可空列,或者XML元素中的可選特性。
可以從一個null類型向任何由System.Nullable<T>類型構造的類型作隱式地轉換。這種轉換的結果就是System.Nullable<T>的預設值。也就是說,可以這樣寫
Nullable<int> x = null;Nullable<string> y = null;
和下面的寫法相同。
Nullable<int> x = Nullable<int>.default;Nullable<string> y = Nullable<string>.default;
20.5.8使用別名指令
使用別名可以命名一個封閉構造類型,但不能命名一個沒有提供類型實參的泛型型別聲明。例如
namespace N1{class A<T>{class B{}}class C{}}namespace N2{using W = N1.A; //錯誤,不能命名泛型型別using X = N1.A.B; //錯誤,不能命名泛型型別using Y = N1.A<int>; //ok,可以命名封閉構造類型using Z = N1.C; //ok}
20.5.9特性
開放類型不能被用於特性內的任何地方。一個封閉構造類型可以被用作特性的實參,但不能被用作特性名,因為System.Attribute不可能是泛型類聲明的基類。
class A:Attribute{public A(Type t){…}}class B<T>: Attribute{} //錯誤,不能將Attribute用作基類class List<T>{[A(typeof(T))] T t; //錯誤,在特性中有開放類型}class X {[A(typeof(List<int>))] int x; //ok,封閉構造類型[B<int>] int y; //錯誤,無效的特性名字}
以上就是C# 2.0 Specification (泛型三)的內容,更多相關內容請關注topic.alibabacloud.com(www.php.cn)!