|
接泛型二 這篇文章是翻譯的微軟的技術(shù)文章.供學(xué)習(xí)c#的朋友參考,請(qǐng)勿用于商業(yè)目的。http://msdn.microsoft.com/vcsharp/team/language/default.aspx 20.4 泛型委托聲明 委托聲明可以包含類型參數(shù)。
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 返回類型 標(biāo)識(shí)符 類型參數(shù)列表可選 (正式參數(shù)列表可選 )類型參數(shù)約束語(yǔ)句可選
使用類型參數(shù)聲明的委托是一個(gè)泛型委托聲明。委托聲明只有在支持類型參數(shù)列表時(shí),才能支持類型參數(shù)約束語(yǔ)句(§20.7)。除了所指出的之外,泛型委托聲明和常規(guī)的委托聲明遵循相同的規(guī)則。泛型委托聲明中的每個(gè)類型參數(shù),在與委托關(guān)聯(lián)的特定聲明空間(§3.3)定義了一個(gè)名字。在委托聲明中的類型參數(shù)的作用域包括返回類型、正式參數(shù)列表和類型參數(shù)約束語(yǔ)句。
像其他泛型類型聲明一樣,必須給定類型實(shí)參以形成構(gòu)造委托類型。構(gòu)造委托類型的參數(shù)和返回值,由委托聲明中構(gòu)造委托類型的每個(gè)類型參數(shù)對(duì)應(yīng)的實(shí)參替代所形成。而結(jié)果返回類型和參數(shù)類型用于確定什么方法與構(gòu)造委托類型兼容。例如
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方法中的兩個(gè)賦值等價(jià)于下面的較長(zhǎng)形式.
static void Main(){
Predicate<int> p1 = new Predicate<int>(F);
Predicate<string> p2 = new Predicate<string>(G);
}
由于方法組轉(zhuǎn)換,較短的形式也是可以的,這在§21.9中有說(shuō)明。
20.5構(gòu)造類型 泛型類型聲明自身并不表示一個(gè)類型。相反,泛型類型聲明通過(guò)應(yīng)用類型實(shí)參的方式被用作形成許多不同類型的“藍(lán)圖”。類型參數(shù)被寫在尖括號(hào)之間,并緊隨泛型類型聲明名字之后。使用至少一個(gè)實(shí)參而被命名的類型被稱為構(gòu)造類型(constructed type)。構(gòu)造類型可以用在語(yǔ)言中類型名字可以出現(xiàn)的大多數(shù)地方。
type-name:(類型名字:)
namespace-or-type-name(命名空間或類型名字)
namespace-or-type-name:(命名空間或類型名字:)
identifier type-argument-list(標(biāo)識(shí)符類型實(shí)參列表可選)
namespace-or-type-name. identifier(命名空間或類型名字.標(biāo)識(shí)符)
type-argument-list opt(類型實(shí)參列表可選)
構(gòu)造類型也能被用在表達(dá)式中作為簡(jiǎn)單名字(§20.9.3)或者訪問一個(gè)成員(§20.9.4)。
當(dāng)一個(gè)命名空間或類型名字被計(jì)算時(shí),只有帶有正確數(shù)量類型參數(shù)的泛型類型會(huì)被考慮。由此,只要類型有不同數(shù)量的類型參數(shù)并且聲明在不同的命名空間,那么使用相同的標(biāo)識(shí)符標(biāo)識(shí)不同的類型是可能的。這對(duì)于在同一程序中混合使用泛型和非泛型類是很有用的。
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.Queue
Queue<int> q2;//System.Collections.Generic.Queue
}
}
在這些代碼中對(duì)于名字查找的詳細(xì)規(guī)則在§20.9中進(jìn)行了描述。在這些代碼中對(duì)于模糊性的決議在§20.6.5中進(jìn)行了描述。
類型名字可能標(biāo)識(shí)一個(gè)構(gòu)造類型,盡管它沒有直接指定類型參數(shù)。這種情況在一個(gè)類型嵌套在一個(gè)泛型類聲明中時(shí)就會(huì)出現(xiàn),并且包含聲明的實(shí)例類型將因?yàn)槊植檎遥ā?0.1.2)而被隱式地使用。
class Outer<T>
{
public class Inner{…}
public Inner i; //i的類型是Outer<T>.Inner
}
在不安全代碼中,構(gòu)造類型不能被用作非托管類型(§18.2)。
20.5.1類型實(shí)參 在一個(gè)類型參數(shù)列表中的每個(gè)實(shí)參都只是一個(gè)類型。
type-argument-list:(類型實(shí)參列表:)
<type-arguments>(<類型實(shí)參>)
type-arguments:(類型實(shí)參:)
type-argument(類型實(shí)參)
type-arguments, type-argument(類型實(shí)參,類型實(shí)參)
type-argument:(類型實(shí)參:)
type(類型)
類型實(shí)參反過(guò)來(lái)也可以是構(gòu)造類型或類型參數(shù)。在不安全代碼中(§18),類型實(shí)參不能是指針類型。每個(gè)類型實(shí)參必須遵循對(duì)應(yīng)類型參數(shù)(§20.7.1)上的任何約束。
20.5.2開放和封閉類型 所有類型都可以被分為開放類型(open type)或封閉類型(closed type)。開放類型是包含類型參數(shù)的類型。更明確的說(shuō)法是
類型參數(shù)定義了一個(gè)開放類型 數(shù)組類型只有當(dāng)其元素是一個(gè)開放類型時(shí)才是開放類型 構(gòu)造類型只有當(dāng)其類型實(shí)參中的一個(gè)或多個(gè)是開放類型時(shí),它才是開放類型
非開放類型都是封閉類型。
在運(yùn)行時(shí),在泛型類型聲明中的所有代碼都在一個(gè)封閉構(gòu)造類型的上下文執(zhí)行,這個(gè)封閉構(gòu)造類型是通過(guò)將類型實(shí)參應(yīng)用到泛型聲明中創(chuàng)建的。在泛型類型中的每個(gè)類型實(shí)參被綁定到一個(gè)特定運(yùn)行時(shí)類型。所有語(yǔ)句和表達(dá)式的運(yùn)行時(shí)處理總是針對(duì)封閉類型發(fā)生,而開放類型只發(fā)生在編譯時(shí)處理。
每個(gè)封閉構(gòu)造類型都有它自己的一組靜態(tài)變量,它們并不被其他封閉類型共享。因?yàn)樵谶\(yùn)行時(shí)不存在開放類型,所以開放類型沒有關(guān)聯(lián)的靜態(tài)變量。如果兩個(gè)封閉構(gòu)造類型是從同一個(gè)類型聲明構(gòu)造的,并且對(duì)應(yīng)的類型實(shí)參也是相同的類型,那么它們就是相同的類型。
20.5.3構(gòu)造類型的基類和接口 構(gòu)造類類型有一個(gè)直接基類,就像是一個(gè)簡(jiǎn)單類類型。如果泛型類聲明沒有指定基類,其基類為object。如果基類在泛型類聲明中被指定,構(gòu)造類型的基類通過(guò)將在基類聲明中的每個(gè)類型參數(shù),替代為構(gòu)造類型對(duì)應(yīng)類型實(shí)參而得到。給定泛型類聲明
class B<U , V>{…}
class G<T>:B<string , T[]>{…}
構(gòu)造類型G<int>的基類將會(huì)是B<string , int[]>。
相似地,構(gòu)造類、結(jié)構(gòu)和接口類型有一組顯式的基接口。顯式基接口通過(guò)接受泛型類型聲明中的顯式基接口聲明和某種替代而形成,這種替代是將在基接口聲明中的每個(gè)類型參數(shù),替代為構(gòu)造類型的對(duì)應(yīng)類型實(shí)參。
一個(gè)類型的所有基類和基接口通過(guò)遞歸地得到中間基類和接口的基類與接口而形成。例如,給定泛型類聲明
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構(gòu)造類型的成員 構(gòu)造類型的非繼承成員通過(guò)替代成員聲明的類型實(shí)參,構(gòu)造類型的對(duì)應(yīng)類型實(shí)參而得到。
例如,給定泛型類聲明
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}{…}
}
構(gòu)造類型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){…}
注意替代處理是基于類型聲明的語(yǔ)義意義的,并不是簡(jiǎn)單的基于文本的替代。在泛型類聲明Gen中的成員a的類型是“T的二維數(shù)組” 因此在先前實(shí)例化類型中的成員a的類型是“int型的一維數(shù)組的二維數(shù)組”或int[,][]。
構(gòu)造類型的繼承成員以一種相似的方法得到。首先直接基類的所有成員是已經(jīng)確定的。如果基類自身是構(gòu)造類型這可能包括當(dāng)前規(guī)則的遞歸應(yīng)用。然后,繼承成員的每一個(gè)通過(guò)將成員聲明中的每個(gè)類型參數(shù),替代為構(gòu)造類型對(duì)應(yīng)類型實(shí)參而被轉(zhuǎn)換。
class B<U>
{
public U F(long index){…}
}
class D<T>:B<T[]>
{
public T G(string s){…}
}
在先前的例子中,構(gòu)造類型D<int>的非繼承成員public int G(string s)通過(guò)替代類型參數(shù)T的類型實(shí)參int而得到。D<int>也有一個(gè)從類聲明B而來(lái)的繼承成員。這個(gè)繼承成員通過(guò)首先確定構(gòu)造類型B<T[]>的成員而被確定,B<T[]>成員的確定是通過(guò)將U替換為替換為T[],產(chǎn)生public T[] F(long index)。然后類型實(shí)參int替換了類型參數(shù)T,產(chǎn)生繼承成員public int[] F(long index)。
20.5.5構(gòu)造類型的可訪問性 當(dāng)構(gòu)造類型C<T1,…,TN>的所有部分C,T1,…,TN 可訪問時(shí),那么它就是可訪問的。例如,如果泛型類型名C是public,并且所有類型參數(shù)T1,…,TN也是public ,那么構(gòu)造類型的可訪問性也是public 。如果類型名或類型實(shí)參之一是private,那么構(gòu)造類型的可訪問性是private。如果類型實(shí)參之一可訪問性是protected,另一個(gè)是internal,那么構(gòu)造類型的可訪問性僅限于該類,以及本程序集之內(nèi)的子類。
20.5.6轉(zhuǎn)換 構(gòu)造類型遵循與非泛型類型相同的規(guī)則(§6)。當(dāng)應(yīng)用這些規(guī)則時(shí),構(gòu)造類型的基類和接口必須按§20.5.3中所描述的方式確定。
除了那些在§6中所描述的之外,構(gòu)造引用類型之間不存在特別的轉(zhuǎn)換。尤其是,不像數(shù)組類型,構(gòu)造引用類型不允許“co-variant”轉(zhuǎn)換。也就是說(shuō),類型List<B>不能轉(zhuǎn)換到類型List<A>(無(wú)論是隱式或顯式)即使是B派生于A也是如此。同樣,也不存在從List<B>到List<object>的轉(zhuǎn)換。
對(duì)于這一點(diǎn)的基本原理是很簡(jiǎn)單的:如果可以轉(zhuǎn)換到List<A>,很顯然你可以存儲(chǔ)一個(gè)類型A的值到這個(gè)list中。這將破壞在List<B>類型中的每個(gè)對(duì)象總是類型B的值這種不變性,或者當(dāng)在集合類上賦值時(shí),將出現(xiàn)不可預(yù)料的錯(cuò)誤。
轉(zhuǎn)換的行為和運(yùn)行時(shí)類型檢查演示如下。
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>是一個(gè)集合
Collection c2 = listB; //OK,List<B>是一個(gè)集合
List<A> a1 = listB; //錯(cuò)誤,沒有隱式的轉(zhuǎn)換
List<A> a2 = (List<A>)listB; //錯(cuò)誤,沒有顯式的轉(zhuǎn)換
}
}
20.5.7System.Nullable<T>類型 在.NET基類庫(kù)中定義了泛型結(jié)構(gòu)類型System.Nullable<T>泛型結(jié)構(gòu)類型,它表示一個(gè)類型T的值可以為null。System.Nullable<T>類型在很多情形下是很有用的,例如用于指示數(shù)據(jù)庫(kù)表的可空列,或者XML元素中的可選特性。
可以從一個(gè)null類型向任何由System.Nullable<T>類型構(gòu)造的類型作隱式地轉(zhuǎn)換。這種轉(zhuǎn)換的結(jié)果就是System.Nullable<T>的默認(rèn)值。也就是說(shuō),可以這樣寫
Nullable<int> x = null;
Nullable<string> y = null;
和下面的寫法相同。
Nullable<int> x = Nullable<int>.default;
Nullable<string> y = Nullable<string>.default;
20.5.8使用別名指令 使用別名可以命名一個(gè)封閉構(gòu)造類型,但不能命名一個(gè)沒有提供類型實(shí)參的泛型類型聲明。例如
namespace N1
{
class A<T>
{
class B{}
}
class C{}
}
namespace N2
{
using W = N1.A; //錯(cuò)誤,不能命名泛型類型
using X = N1.A.B; //錯(cuò)誤,不能命名泛型類型
using Y = N1.A<int>; //ok,可以命名封閉構(gòu)造類型
using Z = N1.C; //ok
}
20.5.9特性 開放類型不能被用于特性內(nèi)的任何地方。一個(gè)封閉構(gòu)造類型可以被用作特性的實(shí)參,但不能被用作特性名,因?yàn)镾ystem.Attribute不可能是泛型類聲明的基類。
class A:Attribute
{
public A(Type t){…}
}
class B<T>: Attribute{} //錯(cuò)誤,不能將Attribute用作基類
class List<T>
{
[A(typeof(T))] T t; //錯(cuò)誤,在特性中有開放類型
}
class X
{
[A(typeof(List<int>))] int x; //ok,封閉構(gòu)造類型
[B<int>] int y; //錯(cuò)誤,無(wú)效的特性名字
}
|