接泛型二
這篇文章是翻譯的微軟的技術文章.供學習c#的朋友參考,請勿用於商業目的。http://msdn.microsoft.com/vcsharp/team/language/default.aspx
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 返回類型 標識符 類型參數列表可選 (正式參數列表可選)類型參數約束語句可選<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
使用類型參數聲明的委託是一個泛型委託聲明。委託聲明只有在支持類型參數列表時,才能支持類型參數約束語句(§20.7)。除了所指出的之外,泛型委託聲明和常規的委託聲明遵循相同的規則。泛型委託聲明中的每個類型參數,在與委託關聯的特定聲明空間(§3.3)定義了一個名字。在委託聲明中的類型參數的作用域包括返回類型、正式參數列表和類型參數約束語句。
像其他泛型類型聲明一樣,必須給定類型實參以形成構造委託類型。構造委託類型的參數和返回值,由委託聲明中構造委託類型的每個類型參數對應的實參替代所形成。而結果返回類型和參數類型用於確定什麼方法與構造委託類型兼容。例如
delegate bool Predicate<T>(T value)
class X
{
static bool F(int i){…}
static bool G(string s){…}
static void <?xml:namespace prefix = st1 ns = "urn:schemas-microsoft-com:office:smarttags" />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.Queue
Queue<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; //錯誤,無效的特性名字
}