.Net泛型類的學(xué)習(xí)總結(jié)
.Net泛型類的學(xué)習(xí)首先我們來看看什么是.Net泛型類?帶有“類型參數(shù)”的類稱為“泛型類”。那么如果使用.Net泛型類,則可為每個(gè)這樣的參數(shù)提供“類型變量”,從而從.Net泛型類生成一個(gè)“構(gòu)造類”。之后可以聲明類型為構(gòu)造類的變量,創(chuàng)建構(gòu)造類的實(shí)例,并將它分配給該變量。除類以外,您還可以定義和使用泛型結(jié)構(gòu)、接口、過程和委托。下面我們就來看看.net泛型類的一些聲明和以及.Net泛型類創(chuàng)建的實(shí)例情況。
.Net泛型類聲明
泛型類聲明是一種類的聲明,它需要提供類型實(shí)參才能構(gòu)成實(shí)際類型。
類聲明可以有選擇地定義類型形參:
- class-declaration:
- attributesopt
- class-modifiersopt
- class identifier
- type-parameter-listopt
- class-baseopt
- type-parameter-constraints-clausesopt
- class-body ;opt
只有提供了一個(gè) type-parameter-list,才可以為這個(gè)類聲明提供 type-parameter-constraints-clauses。
提供了 type-parameter-list 的類聲明是一個(gè)泛型類聲明。此外,任何嵌套在泛型類聲明或泛型結(jié)構(gòu)聲明中的類本身就是一個(gè)泛型類聲明,因?yàn)楸仨殲榘愋吞峁╊愋托螀⒉拍軇?chuàng)建構(gòu)造類型。
除了明確指出的地方外,泛型類聲明與非泛型類聲明遵循相同的規(guī)則。泛型類聲明可嵌套在非泛型類聲明中。
使用構(gòu)造類型 (constructed type)引用泛型類。給定下面的泛型類聲明
- class List﹤T﹥ {}
List﹤T﹥、List﹤int﹥ 和 List﹤List﹤string﹥﹥ 就是其構(gòu)造類型的一些示例。使用一個(gè)或多個(gè)類型形參的構(gòu)造類型(例如 List﹤T﹥)稱為開放構(gòu)造類型 (open constructed type)。不使用類型形參的構(gòu)造類型(例如 List﹤int﹥)稱為封閉構(gòu)造類型 (closed constructed type)。
泛型類型可根據(jù)類型形參的數(shù)目進(jìn)行“重載”;這就是說,同一命名空間或外層類型聲明中的兩個(gè)類型聲明可以使用相同標(biāo)識(shí)符,只要這兩個(gè)聲明具有不同數(shù)目的類型形參即可。
- class C {}
- class C﹤V﹥ {}
- struct C﹤U,V﹥ {} // Error, C with two type parameters defined twice
- class C﹤A,B﹥ {} // Error, C with two type parameters defined twice
類型名稱解析、簡(jiǎn)單名稱解析和成員訪問、過程中使用的類型查找規(guī)則均會(huì)考慮類型形參的數(shù)目。
泛型類聲明的基接口必須滿足唯一性規(guī)則。
.Net泛型類之類型形參
類型形參可在類聲明中提供。每個(gè)類型形參都是一個(gè)簡(jiǎn)單標(biāo)識(shí)符,代表一個(gè)為創(chuàng)建構(gòu)造類型而提供的類型實(shí)參的占位符。類型形參是將來提供的類型的形式占位符。而類型實(shí)參是在創(chuàng)建構(gòu)造類型時(shí)替換類型形參的實(shí)際類型。
- type-parameter-list:
- ﹤ type-parameters ﹥
- type-parameters:
- attributesopt type-parameter
- type-parameters , attributesopt type-parameter
- type-parameter:
- identifier
類聲明中的每個(gè)類型形參在該類的聲明空間中定義一個(gè)名稱。因此,它不能與另一個(gè)類型形參或該類中聲明的成員具有相同的名稱。類型形參不能與類型本身具有相同的名稱。
類上的類型形參的作用域包括 class-base、type-parameter-constraints-clauses 和 class-body。與類的成員不同,此作用域不會(huì)擴(kuò)展到派生類。在其作用域中,類型形參可用作類型。
- type:
- value-type
- reference-type
- type-parameter
由于類型形參可使用許多不同的實(shí)際類型實(shí)參進(jìn)行實(shí)例化,因此類型形參具有與其他類型稍微不同的操作和限制。這包括:
·不能直接使用類型形參聲明基類或接口
·類型形參上的成員查找規(guī)則取決于應(yīng)用到該類型形參的約束(如果有)。
·類型形參的可用轉(zhuǎn)換取決于應(yīng)用到該類型形參的約束(如果有)。
·如果事先不知道由類型形參給出的類型是引用類型不能將標(biāo)識(shí)null 轉(zhuǎn)換為該類型。不過,可以改為使用默認(rèn)值表達(dá)式。此外,具有由類型形參給出的類型的值可以使用 == 和 != 與null 進(jìn)行比較,除非該類型形參具有值類型約束。
·僅當(dāng)類型形參受 constructor-constraint 或值類型約束的約束時(shí),才能將 new 表達(dá)式與類型形參聯(lián)合使用。
·不能在屬性中的任何位置上使用類型形參。
·不能在成員訪問或類型名稱中使用類型形參標(biāo)識(shí)靜態(tài)成員或嵌套類型。
·在不安全代碼中,類型形參不能用作 unmanaged-type。
作為類型,類型形參純粹是一個(gè)編譯時(shí)構(gòu)造。在運(yùn)行時(shí),每個(gè)類型形參都綁定到一個(gè)運(yùn)行時(shí)類型,運(yùn)行時(shí)類型是通過向泛型類型聲明提供類型實(shí)參來指定的。因此,使用類型形參聲明的變量的類型在運(yùn)行時(shí)將是封閉構(gòu)造類型。涉及類型形參的所有語句和表達(dá)式的運(yùn)行時(shí)執(zhí)行都使用作為該形參的類型實(shí)參提供的實(shí)際類型。
.Net泛型類之實(shí)例類型
每個(gè)類聲明都有一個(gè)關(guān)聯(lián)的構(gòu)造類型,即實(shí)例類型 (instance type)。對(duì)于泛型類聲明,實(shí)例類型是通過從該類型聲明創(chuàng)建構(gòu)造類型來構(gòu)成的,所提供的每個(gè)類型實(shí)參替換對(duì)應(yīng)的類型形參。由于實(shí)例類型使用類型形參,因此只能在類型形參的作用域中使用該實(shí)例類型;也就是在類聲明的內(nèi)部。對(duì)于在類聲明中編寫的代碼,實(shí)例類型為 this 的類型。對(duì)于非泛型類,實(shí)例類型就是所聲明的類。下面顯示幾個(gè)類聲明以及它們的實(shí)例類型:
- class A﹤T﹥ // instance type: A﹤T﹥
- {
- class B {} // instance type: A﹤T﹥.B
- class C﹤U﹥ {}// instance type: A﹤T﹥.C﹤U﹥
- }
- class D {} // instance type: D
.Net泛型類之基規(guī)范
類聲明中指定的基類可以是構(gòu)造類類型。基類本身不能是類型形參,但在其作用域中可以包含類型形參。
- class Extend﹤V﹥: V {}
- // Error, type parameter used as base class
泛型類聲明不能使用 System.Attribute 作為直接或間接基類。
類聲明中指定的基接口可以是構(gòu)造接口類型。基接口本身不能是類型形參,但在其作用域中可以包含類型形參。下面的代碼演示類實(shí)現(xiàn)和擴(kuò)展構(gòu)造類型的方法:
- class C﹤U,V﹥ {}
- interface I1﹤V﹥ {}
- class D: C﹤string,int﹥, I1﹤string﹥ {}
- class E﹤T﹥: C﹤int,T﹥, I1﹤T﹥ {}
泛型類聲明的基接口必須滿足唯一性規(guī)則。
如果類中的方法重寫或?qū)崿F(xiàn)基類或接口中的方法,則必須為特定類型提供相應(yīng)的方法。下面的代碼演示如何重寫和實(shí)現(xiàn)方法。對(duì)此做了進(jìn)一步的解釋。
#p#
- class C﹤U,V﹥
- {
- public virtual void M1(U x, List﹤V﹥ y) {}
- }
- interface I1﹤V﹥
- {
- V M2(V);
- }
- class D: C﹤string,int﹥, I1﹤string﹥
- {
- public override void M1(string x, List﹤int﹥ y) {}
- public string M2(string x) {}
- }
.Net泛型類的成員
泛型類的所有成員都可以直接或作為構(gòu)造類型的一部分使用任何包容類 (enclosing class) 中的類型形參。當(dāng)在運(yùn)行時(shí)使用特定的封閉構(gòu)造類型時(shí),所出現(xiàn)的每個(gè)類型形參都被替換成為該構(gòu)造類型提供的實(shí)際類型實(shí)參。例如:
- class C﹤V﹥
- {
- public V f1;
- public C﹤V﹥ f2 = null;
- public C(V x) {
- this.f1 = x;
- this.f2 = this;
- }
- }
- class Application
- {
- static void Main() {
- C﹤int﹥ x1 = new C﹤int﹥(1);
- Console.WriteLine(x1.f1); // Prints 1
- C﹤double﹥ x2 = new C﹤double﹥(3.1415);
- Console.WriteLine(x2.f1); // Prints 3.1415
- }
- }
在實(shí)例函數(shù)成員中,類型 this 是包含這些成員的聲明的實(shí)例類型。
除了使用類型形參作為類型以外,泛型類聲明中的成員與非泛型類的成員遵循相同的規(guī)則。下面幾小節(jié)將討論適用于特定種類的成員的附加規(guī)則。
.Net泛型類中的靜態(tài)字段
泛型類聲明中的靜態(tài)變量在相同封閉構(gòu)造類型的所有實(shí)例之間共享,但是不會(huì)在不同封閉構(gòu)造類型的實(shí)例之間共享。不管靜態(tài)變量的類型是否涉及任何類型形參,這些規(guī)則都適用。
例如:
- class C﹤V﹥
- {
- static int count = 0;
- public C() {
- count++;
- }
- public static int Count {
- get { return count; }
- }
- }
- class Application
- {
- static void Main() {
- C﹤int﹥ x1 = new C﹤int﹥();
- Console.WriteLine(C﹤int﹥.Count); // Prints 1
- C﹤double﹥ x2 = new C﹤double﹥();
- Console.WriteLine(C﹤int﹥.Count); // Prints 1
- C﹤int﹥ x3 = new C﹤int﹥();
- Console.WriteLine(C﹤int﹥.Count); // Prints 2
- }
- }
.Net泛型類中的靜態(tài)構(gòu)造函數(shù)
泛型類中的靜態(tài)構(gòu)造函數(shù)用于初始化靜態(tài)字段,并為從該泛型類聲明創(chuàng)建的每個(gè)不同封閉構(gòu)造類型執(zhí)行其他初始化操作。泛型類型聲明的類型形參處于作用域中,并且可在靜態(tài)構(gòu)造函數(shù)的函數(shù)體中使用。
新的封閉構(gòu)造類類型在***次發(fā)生下列任一情況時(shí)進(jìn)行初始化:
·創(chuàng)建該封閉構(gòu)造類型的實(shí)例。
·引用該封閉構(gòu)造類型的任何靜態(tài)成員。
為了初始化新的封閉構(gòu)造類類型,需要先為該特定的封閉構(gòu)造類型創(chuàng)建一組新的靜態(tài)字段。將其中的每個(gè)靜態(tài)字段初始化為默認(rèn)值。下一步,為這些靜態(tài)字段執(zhí)行靜態(tài)字段初始值設(shè)定項(xiàng)。***,執(zhí)行靜態(tài)構(gòu)造函數(shù)。
由于靜態(tài)構(gòu)造函數(shù)只為每個(gè)封閉構(gòu)造類類型執(zhí)行一次,因此對(duì)于無法通過約束在編譯時(shí)進(jìn)行檢查的類型形參來說,此處是進(jìn)行運(yùn)行時(shí)檢查的方便位置。例如,下面的類型使用靜態(tài)構(gòu)造函數(shù)檢查類型實(shí)參是否為一個(gè)枚舉:
- class Gen﹤T﹥ where T: struct
- {
- static Gen() {
- if (!typeof(T).IsEnum) {
- throw new ArgumentException("T must be an enum");
- }
- }
- }
.Net泛型類之訪問受保護(hù)成員
在泛型類聲明中,通過從該泛型類構(gòu)造的任何類類型的實(shí)例,可以對(duì)繼承的受保護(hù)實(shí)例成員進(jìn)行訪問。具體而言,指定的用于訪問 protected 和 protected internal 實(shí)例成員的規(guī)則通過下面針對(duì)泛型的規(guī)則進(jìn)行了擴(kuò)充:
在泛型類 G 中,如果 E 的類型是從 G 構(gòu)造的類類型或從 G 構(gòu)造的類類型繼承的類類型,則使用 E.M 形式的 primary-expression 訪問繼承的受保護(hù)實(shí)例成員 M 是允許的。
在下面的示例中
- 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 = default(T);
- di.x = 123;
- ds.x = "test";
- }
- }
對(duì) x 的三個(gè)賦值是允許的,因?yàn)樗鼈內(nèi)纪ㄟ^從該泛型類型構(gòu)造的類類型的實(shí)例進(jìn)行。
.Net泛型類中的重載
泛型類聲明中的方法、構(gòu)造函數(shù)、索引器和運(yùn)算符可以被重載。雖然聲明的簽名必須唯一,但是在替換類型實(shí)參時(shí)可能會(huì)導(dǎo)致出現(xiàn)完全相同的簽名。在這樣的情況下,重載解析的附加規(guī)則將挑選最明確的
成員。
下面的示例根據(jù)此規(guī)則演示有效和無效的重載:
- interface I1﹤T﹥ {}
- interface I2﹤T﹥ {}
- class G1﹤U﹥
- {
- int F1(U u); // Overload resulotion for G﹤int﹥.F1
- int F1(int i); // will pick non-generic
- void F2(I1﹤U﹥ a); // Valid overload
- void F2(I2﹤U﹥ a);
- }
- class G2﹤U,V﹥
- {
- void F3(U u, V v);// Valid, but overload resolution for
- void F3(V v, U u);// G2﹤int,int﹥.F3 will fail
- void F4(U u, I1﹤V﹥ v); // Valid, but overload resolution for
- void F4(I1﹤V﹥ v, U u);// G2﹤I1﹤int﹥,int﹥.F4 will fail
- void F5(U u1, I1﹤V﹥ v2); // Valid overload
- void F5(V v1, U u2);
- void F6(ref U u); // valid overload
- void F6(out V v);
- }
形參數(shù)組方法和類型形參
可以在形參數(shù)組的類型中使用類型形參。例如,給定下面的聲明
- class C﹤V﹥
- {
- static void F(int x, int y, params V[] args);
- }
對(duì)該方法的如下展開形式的調(diào)用:
- C﹤int﹥.F(10, 20);
- C﹤object﹥.F(10, 20, 30, 40);
- C﹤string﹥.F(10, 20, "hello", "goodbye");
完全對(duì)應(yīng)于:
- 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"} );
.Net泛型類之重寫和泛型類
和往常一樣,泛型類中的函數(shù)成員可以重寫基類中的函數(shù)成員。在確定被重寫的基成員時(shí),必須通過替換類型實(shí)參來確定基類的成員。一旦確定了基類的成員,重寫規(guī)則就與非泛型類
相同。
下面的示例演示重寫規(guī)則如何在存在泛型的情況下起作用:
- 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() {} // Ok
- public override C﹤string﹥ G() {} // Ok
- public override void H(C﹤T﹥ x) {} // Error, should be C﹤string﹥
- }
- class E﹤T,U﹥: C﹤U﹥
- {
- public override U F() {}// Ok
- public override C﹤U﹥ G() {} // Ok
- public override void H(C﹤T﹥ x) {} // Error, should be C﹤U﹥
- }
.Net泛型類中的運(yùn)算符
泛型類聲明可以定義運(yùn)算符,所遵循的規(guī)則與非泛型類聲明相同。運(yùn)算符聲明中使用類聲明的實(shí)例類型的方式必須與運(yùn)算符的正常使用規(guī)則類似,具體如下:
·一元運(yùn)算符必須以該實(shí)例類型的單個(gè)參數(shù)為操作對(duì)象。一元的 ++ 和 -- 運(yùn)算符必須返回該實(shí)例類型或從該實(shí)例類型派生的類型。
·二元運(yùn)算符的參數(shù)中必須至少有一個(gè)屬于該實(shí)例類型。
·轉(zhuǎn)換運(yùn)算符的形參類型或返回類型必須屬于該實(shí)例類型。
下面演示泛型類中的有效運(yùn)算符聲明的一些示例:
- 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) {}
- }
對(duì)于從源類型 S 轉(zhuǎn)換到目標(biāo)類型 T 的轉(zhuǎn)換運(yùn)算符,在應(yīng)用指定的規(guī)則時(shí),與 S 或 T 關(guān)聯(lián)的任何類型形參都被視為與其他類型沒有繼承關(guān)系的唯一類型,并忽略對(duì)那些類型形參的所有約束。
在下面的示例中
- class C﹤T﹥ {}
- class D﹤T﹥: C﹤T﹥
- {
- public static implicit operator C﹤int﹥(D﹤T﹥ value) {} // Ok
- public static implicit operator C﹤string﹥(D﹤T﹥ value) {} // Ok
- public static implicit operator C﹤T﹥(D﹤T﹥ value) {} // Error
- }
前兩個(gè)運(yùn)算符聲明是允許的,T 和 int 以及 string 分別被視為沒有關(guān)系的唯一類型。但是,第三個(gè)運(yùn)算符是錯(cuò)誤的,因?yàn)?C﹤T﹥ 是 D﹤T﹥ 的基類。
對(duì)于某些類型實(shí)參,可以聲明這樣的運(yùn)算符,即這些運(yùn)算符指定了已經(jīng)作為預(yù)定義轉(zhuǎn)換而存在的轉(zhuǎn)換。在下面的示例中
- struct Convertible﹤T﹥
- {
- public static implicit operator Convertible﹤T﹥(T value) {}
- public static explicit operator T(Convertible﹤T﹥ value) {}
- }
當(dāng)把類型 object 指定為 T 的類型實(shí)參時(shí),第二個(gè)運(yùn)算符將聲明一個(gè)已經(jīng)存在的轉(zhuǎn)換(存在從任何類型到類型 object 的隱式轉(zhuǎn)換,因此也存在顯式轉(zhuǎn)換)。
在兩個(gè)類型之間存在預(yù)定義轉(zhuǎn)換的情況下,這些類型之間的任何用戶定義的轉(zhuǎn)換將被忽略。具體而言:
·如果存在從類型 S 到類型T 的預(yù)定義隱式轉(zhuǎn)換,則從S 到T 的所有用戶定義的轉(zhuǎn)換(隱式或顯式)將被忽略。
·如果存在從類型S 到類型T 的預(yù)定義顯式轉(zhuǎn)換,則從 S 到T 的所有用戶定義的顯式轉(zhuǎn)換將被忽略。但是,仍然會(huì)考慮從 S 到 T 的用戶定義的隱式轉(zhuǎn)換。
對(duì)于除 object 以外的所有類型,上面的 Convertible﹤T﹥ 類型聲明的運(yùn)算符都不會(huì)與預(yù)定義的轉(zhuǎn)換發(fā)生沖突。例如:
- void F(int i, Convertible﹤int﹥ n) {
- i = n; // Error
- i = (int)n; // User-defined explicit conversion
- n = i; // User-defined implicit conversion
- n = (Convertible﹤int﹥)i; // User-defined implicit conversion
- }
但是對(duì)于類型 object,除了下面這個(gè)特例之外,預(yù)定義的轉(zhuǎn)換將在其他所有情況下隱藏用戶定義的
轉(zhuǎn)換:
- void F(object o, Convertible﹤object﹥ n) {
- o = n; // Pre-defined boxing conversion
- o = (object)n; // Pre-defined boxing conversion
- n = o; // User-defined implicit conversion
- n = (Convertible﹤object﹥)o; // Pre-defined unboxing conversion
- }
.Net泛型類中的嵌套類型
泛型類聲明可以包含嵌套的類型聲明。包容類的類型形參可以在嵌套類型中使用。嵌套類型聲明可以包含僅適用于該嵌套類型的附加類型形參。
泛型類聲明中包含的每個(gè)類型聲明都隱式地是泛型類型聲明。在編寫對(duì)嵌套在泛型類型中的類型的引用時(shí),必須指定其包容構(gòu)造類型(包括其類型實(shí)參)。但是可在外層類中不加限定地使用嵌套類型;在構(gòu)造嵌套類型時(shí)可以隱式地使用外層類的實(shí)例類型。下面的示例演示三種不同的引用從 Inner 創(chuàng)建的構(gòu)造類型的正確方法;前兩種方法是等效的:
- class Outer﹤T﹥
- {
- class Inner﹤U﹥
- {
- public static void F(T t, U u) {}
- }
- static void F(T t) {
- Outer﹤T﹥.Inner﹤string﹥.F(t, "abc"); // These two statements have
- Inner﹤string﹥.F(t, "abc"); // the same effect
- Outer﹤int﹥.Inner﹤string﹥.F(3, "abc"); // This type is different
- Outer.Inner﹤string﹥.F(t, "abc");// Error, Outer needs type arg
- }
- }
嵌套類型中的類型形參可以隱藏外層類型中聲明的成員或類型形參,但這是一種不好的編程風(fēng)格:
- class Outer﹤T﹥
- {
- class Inner﹤T﹥ // Valid, hides Outer’s T
- {
- public T t; // Refers to Inner’s T
- }
- }
.Net泛型類的聲明以及使用中會(huì)碰到的一些概念及應(yīng)用的基本內(nèi)容就向你介紹到這里,希望對(duì)你了解和學(xué)習(xí).Net泛型類有所幫助。
【編輯推薦】