C#2.0 Specification(泛型二)

來源:互聯網
上載者:User

20.1.6泛型類中的靜態建構函式

在泛型類中的靜態建構函式被用於初始化靜態欄位,為每個從特定泛型類聲明中建立的不同的封閉構造類型,執行其他初始化。泛型型別聲明的型別參數在範圍之內,可以在靜態建構函式體內被使用。

如果下列情形之一發生,一個新的封閉構造類類型將被首次初始化。

一個封閉構造類型的執行個體被建立時

封閉構造類型的任何靜態成員被引用時

為了初始化一個新的封閉的構造類類型,首先那個特定封閉類型的一組新靜態欄位(§20.1.5)將會被建立。每個靜態欄位都被初始化為其預設值(§5.2)。接著,靜態欄位初始化器(§10.4.5.1)將為這些靜態欄位執行。最後靜態建構函式將被執行。

由於靜態建構函式將為每個封閉構造類類型執行一次,那麼在不能通過約束(§20.7)檢查的型別參數上實施運行時檢查,將會很方便。例如,下面的類型使用一個靜態建構函式檢查一個型別參數是否是一個參考型別。

class Gen<T>{static Gen(){if((object)T.default != null){throw new ArgumentException(“T must be a reference type”);}}}

20.1.7 訪問受保護的成員

在一個泛型類聲明中,對於繼承的受保護的執行個體成員的訪問是允許的,通過從泛型類構造的任何類型的執行個體就可以做到。尤其是,用於訪問§3.5.3中指定的protected和protected internal執行個體成員的規則,對於泛型使用如下的規則進行了擴充。

在一個泛型類G中,對於一個繼承的受保護的執行個體成員M,使用E.M的基本運算式是允許的,前提是E的類型是一個從G構造的類類型,或繼承於一個從G構造的類類型的類類型。



在例子

class C<T>{protected T x;}class D<T> :C<T>{static void F(){D<T> dt = new D<T>();D<int> di = new D<int>();D<string> ds = new D<string>();dt.x = T.default;di.x = 123;ds.x = “test”;}}

三個對x的指派陳述式都是允許的,因為它們都通過從泛型構造的類類型的執行個體發生。

20.1.8在泛型類中重載

在一個泛型類聲明中的方法、建構函式、索引器和運算子可以被重載。但為了避免在構造類中的歧義,這些重載是受約束的。在同一個泛型類聲明中使用相同的名字聲明的兩個函數成員必須具有這樣的參數類型,也就是封閉構造類型中不能出現兩個成員使用相同的名字和簽名。當考慮所有可能的封閉構造類型時,這條規則包含了在當前程式中目前不存在的類型是實參,但它仍然是可能出現的[1]。在型別參數上的類型約束由於這條規則的目的而被忽略了。

下面的例子根據這條規則展示了有效和無效的重載。

nterface I1<T> {…}interface I2<T>{…}class G1<U>{long F1(U u); //無效重載,G<int>將會有使用相同簽名的兩個成員int F1(int i);void F2(U u1, U u2); //有效重載,對於U沒有型別參數void F2(int I , string s); //可能同時是int和string void F3(I1<U>a); //有效重載void F3(I2<U>a);void F4(U a); //有效重載void F4(U[] a);}class G2<U,V>{void F5(U u , V v); //無效重載,G2<int , int>將會有兩個簽名相同的成員void F5(V v, U u);void F6(U u , I1<V> v);//無效重載,G2<I1<int>,int>將會有兩個簽名相同的成員void F6(I1<V> v , U u);void F7(U u1,I1<V> V2);//有效重載,U不可能同時是V和I1<V>void F7(V v1 , U u2);void F8(ref U u); //無效重載void F8(out V v);}class C1{…}class C2{…}class G3<U , V> where U:C1 where V:C2{void F9(U u); //無效重載,當檢查重載時,在U和V上的約束將被忽略void F9(V v);}

0.1.9參數數組方法和型別參數

型別參數可以被用在參數數組的類型中。例如,給定聲明

class C<V>{static void F(int x, int y ,params V[] args);}方法的擴充形式的如下調用C<int>.F(10, 20);C<object>.F(10,20,30,40);C<string>.F(10,20,”hello”,”goodbye”);對應於如下形式:C<int>.F(10,20, new int[]{});C<object>.F(10,20,new object[]{30,40});C<string>.F(10,20,new string[](“hello”,”goodbye”));

20.1.10重寫和泛型類

在泛型類中的函數成員可以重寫基類中的函數成員。如果基類是一個非泛型型別或封閉構造類型,那麼任何重寫函數成員不能有包含型別參數的組成類型。然而,如果一個基類是一個開放構造類型,那麼重寫函數成員可以使用在其聲明中的型別參數。當重寫基類成員時,基類成員必須通過替換類型實參而被確定,如§20.5.4中所描述的。一旦基類的成員被確定,用於重寫的規則和非泛型類是一樣的。

下面的例子示範了對於現有的泛型其重寫規則是如何工作的。

abstract class C<T>{public virtual T F(){…}public virtual C<T> G(){…}public virtual void H(C<T> x ){…}} class D:C<string>{public override string F(){…}//OKpublic override C<string> G(){…}//OKpublic override void H(C<T> x); //錯誤,應該是C<string>}class E<T,U>:C<U>{public override U F(){…}//OKpublic override C<U> G(){…}//OKpublic override void H(C<T> x){…}//錯誤,應該是C<U>}

20.1.11泛型類中的運算子

泛型類聲明可以定義運算子,它遵循和常規類相同的規則。類聲明的執行個體類型(§20.1.2)必須以一種類似於運算子的常規規則的方式,在運算子聲明中被使用,如下

一元運算子必須接受一個執行個體類型的單一參數。一元運算子“++”和“—”必須返回執行個體類型。

至少二元運算子的參數之一必須是執行個體類型。

轉換運算子的參數類型和傳回型別都必須是執行個體類型。


下面展示了在泛型類中幾個有效運算子聲明的例子

class X<T>{public static X<T> operator ++(X(T) operand){…}public static int operator *(X<T> op1, int op2){…}public static explicit operator X<T>(T value){…}}

對於一個從源類型S到目標類型T的轉換運算子,當應用§10.9.3中的規則時,任何關聯S或T的型別參數被認為是唯一類型,它們與其他類型沒有繼承關係,並且在這些型別參數上的任何約束都將被忽略。

在例子

class C<T>{…}class D<T>:C<T>{public static implicit operator C<int>(D<T> value){…}//OKpublic static implicit operator C<T>(D<T> value){…}//錯誤}

第一個運算子聲明是允許的,由於§10.9.3的原因,T和int被認為是沒有關係的唯一類型。然而,第二個運算子是一個錯誤,因為C<T>是D<T>的基類。

給定先前的例子,為某些類型實參聲明運算子,指定已經作為預定義轉換而存在的轉換是可能的。

struct Nullable<T>{public static implicit operator Nullable<T>(T value){…}public static explicit operator T(Nullable<T> value){…}}

當類型object作為T的類型實參被指定,第二個運算子聲明了一個已經存在的轉換(從任何類型到object是一個隱式的,也可以是顯式的轉換)。

在兩個類型之間存在預定義的轉換的情形下,在這些類型上的任何使用者定義的轉換都將被忽略。尤其是

如果存在從類型S到類型T的預定義的隱式轉換(§6.1),所有使用者定義的轉換(隱式的或顯式的)都將被忽略。

如果存在從類型S到類型T的預定義的顯式轉換,那麼任何使用者定義的從類型S到類型T的顯式轉換都將被忽略。但使用者定義的從S到T的隱式轉換仍會被考慮。

對於所有類型除了object,由Nullable<T>型別宣告的運算子都不會與預定義的轉換衝突。例如

void F(int I , Nullable<int> n){i = n; //錯誤i = (int)n; //使用者定義的顯式轉換n = i; //使用者定義的隱式轉換n = (Nullable<int>)i; //使用者定義的隱式轉換}

然而,對於類型object,預定義的轉換在所有情況隱藏了使用者定義轉換,除了一種情況:

void F(object o , Nullable<object> n){o = n; //預定義裝箱轉換o= (object)n; //預定義裝箱轉換n= o; //使用者定義隱式轉換n = (Nullable<object>)o; //預定義unboxing轉換}


20.1.12泛型類中的巢狀型別

泛型類聲明可以包含巢狀型別聲明。封閉類的型別參數可以在巢狀型別中使用。巢狀型別聲明可以包含附加的型別參數,它只適用於該巢狀型別。

包含在泛型類聲明中的每個型別宣告是隱式的泛型型別聲明。當編寫一個嵌套在泛型型別內的類型的引用時,包含構造類型,包括它的類型實參,必須被命名。然而,在外部類中,內部類型可以被無限制的使用;當構造一個內部類型時,外部類的執行個體類型可以被隱式地使用。下面的例子展示了三個不同的引用從Inner建立的構造類型的方法,它們都是正確的;前兩個是等價的。
class Outer<T>{class Inner<U>{static void F(T t , U u){…}}static void F(T t){Outer<T>.Inner<string >.F(t,”abc”);//這兩個語句有同樣的效果Inner<string>.F(t,”abc”);Outer<int>.Inner<string>.F(3,”abc”); //這個類型是不同的Outer.Inner<string>.F(t , “abc”); //錯誤,Outer需要型別參數}}

儘管這是一種不好的編程風格,但巢狀型別中的型別參數可以隱藏一個成員,或在外部類型中聲明的一個型別參數。

class Outer<T>{class Inner<T> //有效,隱藏了 Ouer的 T{public T t; //引用Inner的T}}

20.1.13應用程式進入點

應用程式進入點不能在一個泛型類聲明中。

20.2泛型結構聲明

像類聲明一樣,結構聲明可以有可選的型別參數。

struct-declaration:(結構聲明:)attributes opt struct-modifiers opt struct identifier type-parameter-list opt struct-interfaces opt type-parameter-constraints-clauses opt struct-body ;opt(特性可選 結構修飾符可選 struct 標識符 型別參數列表可選 結構介面可選 型別參數約束語句可選 結構體;可選)

除了§11.3中為結構聲明而指出的差別之外,泛型類聲明的規則也適用於泛型結構聲明。

20.3泛型介面聲明

介面也可以定義可選的型別參數

interface-declaration:(介面聲明:)attribute opt interface-modifiers opt interface indentifier type-parameter-list opt interface-base opt type-parameter-constraints-clause opt interface-body;(特性可選 介面修飾符可選 interface 標識符 型別參數列表可選 基底介面可選 型別參數約束語句可選 介面體;可選)使用型別參數聲明的介面是一個泛型介面聲明。除了所指出的那些,泛型介面聲明遵循和常規結構聲明相同的規則。

在介面聲明中的每個型別參數在介面的聲明空間定義了一個名字。在一個介面上的型別參數的範圍包括基底介面、類型約束語句和介面體。在其範圍之內,一個型別參數可以被用作一個類型。應用到介面上的型別參數和應用到類(§20.1.1)上的型別參數具有相同的限制。

在泛型介面中的方法與泛型類(§20.1.8)中的方法遵循相同的重載規則。

20.3.1實現介面的唯一性

由泛型型別聲明實現的介面必須為所有可能的構造類型保留唯一性。沒有這條規則,將不可能為特定的構造類型確定調用正確的方法。例如,假定一個泛型類聲明允許如下寫法。

interface I<T>{void F();}class X<U, V>:I<U>,I<V> //錯誤,I<U>和I<V>衝突{void I<U>.F(){…}void I<V>.F(){…}}

如果允許這麼寫,那麼下面的情形將無法確定執行那段代碼。

I<int> x = new X<int ,int>();x.F();

為了確定一個泛型型別聲明的介面列表是有效,可以按下面的步驟進行。

讓L成為在泛型類、結構或介面聲明 C中指定的介面的列表。

將任何已經在L中的介面的基底介面添加到L

從L中刪除任何重複的介面

在類型實參被替換到L後,如果任何從C建立的可能構造類型,導致在L中的兩個介面是同一的,那麼C的聲明是無效的。當確定所有可能的構造類型時,約束聲明不予考慮。


在類聲明X之上,介面列表L由I<U>和I<V>組成。該聲明是無效的,因為任何使用相同類型U和V的構造類型,將導致這兩個介面是同一的。

20.3.2顯式介面成員實現

使用構造介面類型的顯式介面成員實現本質上與簡單介面類型方式上是相同的。和以往一樣,顯式介面成員實現必須由一個指明哪個介面被實現的介面類型而限定。該類型可能是一個簡單介面或構造介面,如下例子所示。

interface IList<T>{T[] GetElement();}interface IDictionary<K,V>{V this[K key];Void Add(K key , V value);}class List<T>:IList<T>,IDictionary<int , T>{T[] IList<T>.GetElement(){…}T IDictionary<int , T>.this[int index]{…}void IDictionary<int , T>.Add(int index , T value){…}}

[1] 也就是在型別參數被替換成類型實參時,有可能替換後的實參導致出現兩個成員使用相同的名字和簽名。


以上就是C#2.0 Specification(泛型二)的內容,更多相關內容請關注topic.alibabacloud.com(www.php.cn)!

  • 相關文章

    聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.