C# 語(yǔ)言的類型劃分為兩大類:值類型 (Value type) 和援用類型 (reference type)。值類型和援用類型都可以為泛型類型 (generic type),泛型類型采取1個(gè)或多個(gè)類型參數(shù)。類型參數(shù)可以指定值類型和援用類型。
type:
value-type
reference-type
type-parameter
第3種類型是指針,只能用在不安全代碼中。第 18.2 節(jié)對(duì)此做了進(jìn)1步的探討。
值類型與援用類型的不同的地方在于:值類型的變量直接包括其數(shù)據(jù),而援用類型的變量存儲(chǔ)對(duì)其數(shù)據(jù)的援用 (reference),后者稱為對(duì)象 (object)。對(duì)援用類型,兩個(gè)變量可能援用同1個(gè)對(duì)象,因此對(duì)1個(gè)變量的操作可能影響另外一個(gè)變量所援用的對(duì)象。對(duì)值類型,每一個(gè)變量都有自己的數(shù)據(jù)副本,對(duì)1個(gè)變量的操作不可能影響另外一個(gè)變量。
C# 的類型系統(tǒng)是統(tǒng)1的,因此任何類型的值都可以按對(duì)象處理。C# 中的每一個(gè)類型直接或間接地從 object 類類型派生,而 object 是所有類型的終究基類。援用類型的值都被視為 object 類型,被簡(jiǎn)單地當(dāng)作對(duì)象來處理。值類型的值則通過對(duì)其履行裝箱和拆箱操作(第 4.3 節(jié))按對(duì)象處理。
1.1 值類型
1個(gè)值類型或是結(jié)構(gòu)類型,或是枚舉類型。C# 提供稱為簡(jiǎn)單類型 (simple type) 的預(yù)定義結(jié)構(gòu)類型集。簡(jiǎn)單類型通過保存字標(biāo)識(shí)。
value-type:
struct-type
enum-type
struct-type:
type-name
simple-type
nullable-type
simple-type:
numeric-type
bool
numeric-type:
integral-type
floating-point-type
decimal
integral-type:
sbyte
byte
short
ushort
int
uint
long
ulong
char
floating-point-type:
float
double
nullable-type:
non-nullable-value-type ?
non-nullable-value-type:
type
enum-type:
type-name
與援用類型的變量不同的是,僅當(dāng)該值類型是可以為 null 的類型時(shí),值類型的變量才可包括 null 值。 對(duì)每一個(gè)不可以為 null 的值類型,都存在1個(gè)對(duì)應(yīng)的可以為 null 的值類型,該類型表示相同的值集加上 null 值。
對(duì)值類型變量賦值時(shí),會(huì)創(chuàng)建所賦的值的1個(gè)副本。這不同于援用類型的變量賦值,援用類型的變量賦值復(fù)制的是援用而不是由援用標(biāo)識(shí)的對(duì)象。
1.1.1 System.ValueType 類型
所有值類型從類 System.ValueType 隱式繼承,后者又從類 object 繼承。任何類型都不可能從值類型派生,因此,所有值類型都是隱式密封的(第 10.1.1.2 節(jié))。
注意,System.ValueType 本身不是 value-type, 而是 class-type,所有 value-type 都從它自動(dòng)派生。
1.1.2 默許構(gòu)造函數(shù)
所有值類型都隱式聲明1個(gè)稱為默許構(gòu)造函數(shù) (default constructor) 的公共無參數(shù)實(shí)例構(gòu)造函數(shù)。默許構(gòu)造函數(shù)返回1個(gè)零初始化實(shí)例,它就是該值類型的默許值 (default value):
class A
{
void F() {
int i = 0;
int j = new int();
}
}
由于每一個(gè)值類型都隱式地具有1個(gè)公共無形參實(shí)例構(gòu)造函數(shù),因此,1個(gè)結(jié)構(gòu)類型中不可能包括1個(gè)關(guān)于無形參構(gòu)造函數(shù)的顯式聲明。但允許結(jié)構(gòu)類型聲明參數(shù)化實(shí)例構(gòu)造函數(shù)(第 11.3.8 節(jié))。
1.1.3 結(jié)構(gòu)類型
結(jié)構(gòu)類型是1種值類型,它可以聲明常量、字段、方法、屬性、索引器、運(yùn)算符、實(shí)例構(gòu)造函數(shù)、靜態(tài)構(gòu)造函數(shù)和嵌套類型。結(jié)構(gòu)類型的聲明在第 11.1 節(jié)中說明。
1.1.4 簡(jiǎn)單類型
C# 提供稱為簡(jiǎn)單類型 (simple type) 的預(yù)定義結(jié)構(gòu)類型集。簡(jiǎn)單類型通過保存字標(biāo)識(shí),而這些保存字只是 System 命名空間中預(yù)定義結(jié)構(gòu)類型的別名,詳見下表。
.. |
化名的類型 |
sbyte |
System.SByte |
byte |
System.Byte |
short |
System.Int16 |
ushort |
System.UInt16 |
int |
System.Int32 |
uint |
System.UInt32 |
long |
System.Int64 |
ulong |
System.UInt64 |
char |
System.Char |
float |
System.Single |
double |
System.Double |
bool |
System.Boolean |
decimal |
System.Decimal |
由于簡(jiǎn)單類型是結(jié)構(gòu)類型的別名,所以每一個(gè)簡(jiǎn)單類型都具有成員。例如,int 具有在 System.Int32 中聲明的成員和從 System.Object 繼承的成員,允許使用下面的語(yǔ)句:
int i = int.MaxValue; // System.Int32.MaxValue constant
string s = i.ToString(); // System.Int32.ToString() instance method
string t = 123.ToString(); // System.Int32.ToString() instance method
簡(jiǎn)單類型與其他結(jié)構(gòu)類型的不同的地方在于,簡(jiǎn)單類型允許某些附加的操作:
1.1.5 整型
整型1元運(yùn)算符和2元運(yùn)算符總是對(duì)有符號(hào) 32 位精度、無符號(hào)的 32 位精度、有符號(hào) 64 位精度或無符號(hào) 64 位精度進(jìn)行計(jì)算:
char 類型歸類為整型類型,但它在以下兩個(gè)方面不同于其他整型:
checked 和 unchecked 運(yùn)算符和語(yǔ)句用于控制整型算術(shù)運(yùn)算和轉(zhuǎn)換(第 7.6.12 節(jié))的溢出檢查。在 checked 上下文中,溢生產(chǎn)生編譯時(shí)毛病或致使引發(fā) System.OverflowException。在 unchecked 上下文中將疏忽溢出,任何與目標(biāo)類型不匹配的高序位都被放棄。
1.1.6 浮點(diǎn)型
C# 支持兩種浮點(diǎn)型:float 和 double。float 和 double 類型用 32 位單精度和 64 位雙精度 IEEE 754 格式來表示,這些格式提供以下幾組值:
float 類型可表示精度為 7 位、在大約 1.5 × 10?45 到 3.4 × 1038 的范圍內(nèi)的值。
double 類型可表示精度為 15 位或 16 位、在大約 5.0 × 10?324 到 1.7 × 10308 的范圍內(nèi)的值。
如果2元運(yùn)算符的1個(gè)操作數(shù)為浮點(diǎn)型,則另外一個(gè)操作數(shù)必須為整型或浮點(diǎn)型,并且運(yùn)算按下面這樣計(jì)算:
浮點(diǎn)運(yùn)算符(包括賦值運(yùn)算符)歷來不產(chǎn)生異常。相反,在異常情況下,浮點(diǎn)運(yùn)算產(chǎn)生零、無窮大或 NaN,以下所述:
可以用比運(yùn)算的結(jié)果類型更高的精度來履行浮點(diǎn)運(yùn)算。例如,某些硬件結(jié)構(gòu)支持比 double 類型具有更大的范圍和精度的“extended”或“l(fā)ong double”浮點(diǎn)型,并隱式地使用這類更高精度類型履行所有浮點(diǎn)運(yùn)算。只有性能開消過大,才能使這樣的硬件結(jié)構(gòu)用“較低”的精度履行浮點(diǎn)運(yùn)算。C# 采取的是允許將更高的精度類型用于所有浮點(diǎn)運(yùn)算,而不是強(qiáng)求履行規(guī)定的精度,造成同時(shí)損失性能和精度。除傳遞更精確的結(jié)果外,這樣做很少會(huì)產(chǎn)生任何可發(fā)覺的效果。但是,在 x * y / z 情勢(shì)的表達(dá)式中,如果其中的乘法會(huì)產(chǎn)生超越 double 范圍的結(jié)果,而后面的除法使臨時(shí)結(jié)果返回到 double 范圍內(nèi),則以更大范圍的格式去計(jì)算該表達(dá)式,可能會(huì)產(chǎn)生有限值的結(jié)果(本來應(yīng)是無窮大)。
1.1.7 decimal 類型
decimal 類型是 128 位的數(shù)據(jù)類型,合適用于財(cái)務(wù)計(jì)算和貨幣計(jì)算。decimal 類型可以表示具有 28 或 29 個(gè)有效數(shù)字、從 1.0 × 10?28 到大約 7.9 × 1028 范圍內(nèi)的值。
decimal 類型的有限值集的情勢(shì)為 (–1)s × c × 10-e,其中符號(hào) s 是 0 或 1,系數(shù) c 由 0 ≤ c < 296 給定,小數(shù)位數(shù) e 滿足 0 ≤ e ≤ 28。decimal 類型不支持有符號(hào)的零、無窮大或 NaN。decimal 可用1個(gè)以 10 的冪表示的 96 位整數(shù)來表示。對(duì)絕對(duì)值小于 1.0m 的 decimal,它的值最多精確到第 28 位小數(shù)。對(duì)絕對(duì)值大于或等于 1.0m 的 decimal,它的值精確到小數(shù)點(diǎn)后第 28 或 29 位。與 float 和 double 數(shù)據(jù)類型相反,10進(jìn)制小數(shù)數(shù)字(如 0.1)可以精確地用 decimal 表示情勢(shì)來表示。在 float 和 double 表示情勢(shì)中,這類數(shù)字通常變成無窮小數(shù),使這些表示情勢(shì)更容易產(chǎn)生舍入毛病。
如果2元運(yùn)算符的1個(gè)操作數(shù)為 decimal 類型,則另外一個(gè)操作數(shù)必須為整型或 decimal 類型。如果存在1個(gè)整型操作數(shù),它將在履行運(yùn)算前轉(zhuǎn)換為 decimal。
decimal 類型值的運(yùn)算結(jié)果是這樣得出的:先計(jì)算1個(gè)精確結(jié)果(按每一個(gè)運(yùn)算符的定義保存小數(shù)位數(shù)),然后舍入以合適表示情勢(shì)。結(jié)果舍入到最接近的可表示值,當(dāng)結(jié)果一樣地接近于兩個(gè)可表示值時(shí),舍入到最小有效位數(shù)位置中為偶數(shù)的值(這稱為“銀行家舍入法”)。零結(jié)果總是包括符號(hào) 0 和小數(shù)位數(shù) 0。
如果10進(jìn)制算術(shù)運(yùn)算產(chǎn)生1個(gè)絕對(duì)值小于或等于 5 × 10⑵9 的值,則運(yùn)算結(jié)果變成零。如果 decimal 算術(shù)運(yùn)算產(chǎn)生的值對(duì) decimal 格式太大,則將引發(fā) System.OverflowException。
與浮點(diǎn)型相比,decimal 類型具有較高的精度,但取值范圍較小。因此,從浮點(diǎn)型到 decimal 的轉(zhuǎn)換可能會(huì)產(chǎn)生溢出異常,而從 decimal 到浮點(diǎn)型的轉(zhuǎn)換則可能致使精度損失。由于這些緣由,在浮點(diǎn)型和 decimal 之間不存在隱式轉(zhuǎn)換,如果沒有顯式地標(biāo)出強(qiáng)迫轉(zhuǎn)換,就不可能在同1表達(dá)式中同時(shí)使用浮點(diǎn)操作數(shù)和 decimal 操作數(shù)。
1.1.8 bool 類型
bool 類型表示布爾邏輯量。bool 類型的可能值為 true 和 false。
在 bool 和其他類型之間不存在標(biāo)準(zhǔn)轉(zhuǎn)換。具體而言,bool 類型與整型截然不同,不能用 bool 值代替整數(shù)值,反之亦然。
在 C 和 C++ 語(yǔ)言中,零整數(shù)或浮點(diǎn)值或 null 指針可以轉(zhuǎn)換為布爾值 false,非零整數(shù)或浮點(diǎn)值或非 null 指針可以轉(zhuǎn)換為布爾值 true。在 C# 中,這類轉(zhuǎn)換是通過顯式地將整數(shù)或浮點(diǎn)值與零進(jìn)行比較,或顯式地將對(duì)象援用與 null 進(jìn)行比較來完成的。
1.1.9 枚舉類型
枚舉類型是具有命名常量的獨(dú)特的類型。每一個(gè)枚舉類型都有1個(gè)基礎(chǔ)類型,該基礎(chǔ)類型必須為 byte、sbyte、short、ushort、int、uint、long 或 ulong。枚舉類型的值集和它的基礎(chǔ)類型的值集相同。枚舉類型的值其實(shí)不只限于那些命名常量的值。枚舉類型是通過枚舉聲明(第 14.1 節(jié))定義的。
1.1.10 可以為 null 的類型
可以為 null 的類型可以表示其基礎(chǔ)類型 (underlying type) 的所有值和1個(gè)額外的 null 值。可以為 null 的類型寫作 T?,其中 T 是基礎(chǔ)類型。此語(yǔ)法是 System.Nullable的簡(jiǎn)寫情勢(shì),這兩種情勢(shì)可以互換使用。
相反,不可以為 null 的值類型 (non-nullable value type) 可以是除 System.Nullable及其簡(jiǎn)寫情勢(shì)T?(對(duì)任何類型的 T)以外的任何值類型,加上束縛為不可以為 null 的值類型的任何類型參數(shù)(即具有 struct 束縛的任何類型參數(shù))。System.Nullable類型指定 T 的值類型束縛(第 10.1.5 節(jié)),這意味著可以為 null 的類型的基礎(chǔ)類型可以是任何不可以為 null 的值類型。可以為 null 的類型的基礎(chǔ)類型不能是可以為 null 的類型或援用類型。例如,int?? 和 string? 是無效類型。
可以為 null 的類型 T? 的實(shí)例有兩個(gè)公共只讀屬性:
HasValue 為 true 的實(shí)例稱為非 null。非 null 實(shí)例包括1個(gè)已知值,可通過 Value 返回該值。
HasValue 為 false 的實(shí)例稱為 null。null 實(shí)例有1個(gè)不肯定的值。嘗試讀取 null 實(shí)例的 Value 將致使引發(fā) System.InvalidOperationException。訪問可以為 null 的實(shí)例的 Value 屬性的進(jìn)程稱作解包 (unwrapping)。
除默許構(gòu)造函數(shù)以外,每一個(gè)可以為 null 的類型 T? 都有1個(gè)具有類型為 T 的單個(gè)實(shí)參的公共構(gòu)造函數(shù)。例如,給定1個(gè)類型為 T 的值 x,調(diào)用形如
new T?(x)
的構(gòu)造函數(shù)將創(chuàng)建 T? 的非 null 實(shí)例,其 Value 屬性為 x。為1個(gè)給定值創(chuàng)建可以為 null 的類型的非 null 實(shí)例的進(jìn)程稱作包裝 (wrapping)。
從 null 文本轉(zhuǎn)換為 T?(第 6.1.5 節(jié))和從 T 轉(zhuǎn)換為 T?(第 6.1.4 節(jié))可以使用隱式轉(zhuǎn)換。
1.2 援用類型
援用類型是類類型、接口類型、數(shù)組類型或拜托類型。
reference-type:
class-type
interface-type
array-type
delegate-type
class-type:
type-name
object
dynamic
string
interface-type:
type-name
array-type:
non-array-type rank-specifiers
non-array-type:
type
rank-specifiers:
rank-specifier
rank-specifiers rank-specifier
rank-specifier:
[ dim-separatorsopt ]
dim-separators:
,
dim-separators ,
delegate-type:
type-name
援用類型值是對(duì)該類型的某個(gè)實(shí)例 (instance) 的1個(gè)援用,后者稱為對(duì)象 (object)。null 值比較特別,它兼容于所有援用類型,用來表示“沒有被援用的實(shí)例”。
1.2.1 類類型
類類型定義包括數(shù)據(jù)成員、函數(shù)成員和嵌套類型的數(shù)據(jù)結(jié)構(gòu),其中數(shù)據(jù)成員包括常量和字段,函數(shù)成員包括方法、屬性、事件、索引器、運(yùn)算符、實(shí)例構(gòu)造函數(shù)、析構(gòu)函數(shù)和靜態(tài)構(gòu)造函數(shù)。類類型支持繼承,繼承是派生類可用來擴(kuò)大和專門化基類的1種機(jī)制。類類型的實(shí)例是用 object-creation-expressions(第 7.6.10.1 節(jié))創(chuàng)建的。
有關(guān)類類型的介紹詳見第 10 章。
某些預(yù)定義類類型在 C# 語(yǔ)言中有特殊含義,以下表所示。
類類型 |
說明 |
System.Object |
所有其他類型的終究基類。請(qǐng)參見第 4.2.2 節(jié)。 |
System.String |
C# 語(yǔ)言的字符串類型。請(qǐng)參見第 4.2.4 節(jié)。 |
System.ValueType |
所有值類型的基類。請(qǐng)參見第 4.1.1 節(jié)。 |
System.Enum |
所有枚舉類型的基類。請(qǐng)參見第 14 章。 |
System.Array |
所有數(shù)組類型的基類。請(qǐng)參見第 12 章。 |
System.Delegate |
所有拜托類型的基類。請(qǐng)參見第 15 章。 |
System.Exception |
所有異常類型的基類。請(qǐng)參見第 16 章。 |
1.2.2 對(duì)象類型
object 類類型是所有其他類型的終究基類。C# 中的每種類型都是直接或間接從 object 類類型派生的。
關(guān)鍵字 object 只是預(yù)定義類 System.Object 的別名。
1.2.3 dynamic 類型
dynamic 類型與 object 1樣,可以援用任何對(duì)象。在將運(yùn)算符利用于 dynamic 類型的表達(dá)式時(shí),其解析會(huì)推延到程序運(yùn)行時(shí)進(jìn)行。因此,如果運(yùn)算符不能合法地利用于援用的對(duì)象,在編譯進(jìn)程中不會(huì)報(bào)告任何毛病。而是在運(yùn)行時(shí)解析運(yùn)算符失敗時(shí),會(huì)引發(fā)異常。
在第 4.7 節(jié)中進(jìn)1步介紹了動(dòng)態(tài)類型,在第 7.2.2 節(jié)中進(jìn)1步介紹了動(dòng)態(tài)綁定。
1.2.4 string 類型
string 類型是直接從 object 繼承的密封類類型。string 類的實(shí)例表示 Unicode 字符串。
string 類型的值可以寫為字符串(第 2.4.4.5 節(jié))。
關(guān)鍵字 string 只是預(yù)定義類 System.String 的別名。
1.2.5 接口類型
1個(gè)接口定義1個(gè)協(xié)議。實(shí)現(xiàn)某接口的類或結(jié)構(gòu)必須遵照該接口定義的協(xié)議。1個(gè)接口可以從多個(gè)基接口繼承,而1個(gè)類或結(jié)構(gòu)可以實(shí)現(xiàn)多個(gè)接口。
有關(guān)接口類型的介紹詳見第 13 章。
1.2.6 數(shù)組類型
數(shù)組是1種數(shù)據(jù)結(jié)構(gòu),它包括可通過計(jì)算索引訪問的零個(gè)或更多個(gè)變量。數(shù)組中包括的變量(又稱數(shù)組的元素)具有相同的類型,該類型稱為數(shù)組的元素類型。
有關(guān)數(shù)組類型的介紹詳見第 12 章。
1.2.7 拜托類型
拜托是援用1個(gè)或多個(gè)方法的數(shù)據(jù)結(jié)構(gòu)。對(duì)實(shí)例方法,拜托還可援用實(shí)例方法對(duì)應(yīng)的對(duì)象實(shí)例。
在 C 或 C++ 中與拜托最接近的是函數(shù)指針,但函數(shù)指針只能援用靜態(tài)函數(shù),而拜托則既可以援用靜態(tài)方法,也能夠援用實(shí)例方法。在后1種情況中,拜托不但存儲(chǔ)了1個(gè)對(duì)該方法入口點(diǎn)的援用,還存儲(chǔ)了1個(gè)對(duì)相應(yīng)的對(duì)象實(shí)例的援用,該方法就是通過此對(duì)象實(shí)例被調(diào)用的。
有關(guān)拜托類型的介紹詳見第 15 章。
1.3 裝箱和拆箱
裝箱和拆箱的概念是 C# 的類型系統(tǒng)的核心。它在 value-types 和 reference-types 之間架起了1座橋梁,使得任何 value-type 的值都可以轉(zhuǎn)換為 object 類型的值,反過來轉(zhuǎn)換也能夠。裝箱和拆箱使我們能夠統(tǒng)1地來考察類型系統(tǒng),其中任何類型的值終究都可以按對(duì)象處理。
1.3.1 裝箱轉(zhuǎn)換
裝箱轉(zhuǎn)換允許將 value-type 隱式轉(zhuǎn)換為 reference-type。存在以下裝箱轉(zhuǎn)換:
請(qǐng)注意,對(duì)類型形參進(jìn)行隱式轉(zhuǎn)換將以裝箱轉(zhuǎn)換的情勢(shì)履行(如果在運(yùn)行時(shí)它最后從值類型轉(zhuǎn)換到援用類型(第 6.1.10 節(jié)))。
將 non-nullable-value-type 的1個(gè)值裝箱包括以下操作:分配1個(gè)對(duì)象實(shí)例,然后將 non-nullable-value-type 的值復(fù)制到該實(shí)例中。
對(duì) nullable-type 的值裝箱時(shí),如果該值為 null 值(HasValue 為 false),將產(chǎn)生1個(gè) null 援用;否則將產(chǎn)生對(duì)基礎(chǔ)值解包和裝箱的結(jié)果。
最能說明 non-nullable-value-type 的值的實(shí)際裝箱進(jìn)程的辦法是,假想有1個(gè)泛型裝箱類 (boxing class),其行動(dòng)與下面聲明的類相似:
sealed class Box: System.ValueType
{
T value;
public Box(T t) {
value = t;
}
}
T 類型值 v 的裝箱進(jìn)程現(xiàn)在包括履行表達(dá)式 new Box(v) 和將結(jié)果實(shí)例作為 object 類型的值返回。因此,下面的語(yǔ)句
int i = 123;
object box = i;
在概念上相當(dāng)于
int i = 123;
object box = new Box(i);
實(shí)際上,像上面這樣的 Box裝箱類其實(shí)不存在,并且裝箱值的動(dòng)態(tài)類型也不會(huì)真的屬于1個(gè)類類型。相反,T 類型的裝箱值屬于動(dòng)態(tài)類型 T,若用 is 運(yùn)算符來檢查動(dòng)態(tài)類型,也僅能援用類型 T。例如,
int i = 123;
object box = i;
if (box is int) {
Console.Write("Box contains an int");
}
將在控制臺(tái)上輸出字符串“Box contains an int”。
裝箱轉(zhuǎn)換隱含著復(fù)制1份 待裝箱的值。這不同于從 reference-type 到 object 類型的轉(zhuǎn)換,在后1種轉(zhuǎn)換中,轉(zhuǎn)換后的值繼續(xù)援用同1實(shí)例,只是將它當(dāng)作派生程度較小的 object 類型而已。例如,給定下面的聲明
struct Point
{
public int x, y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
則下面的語(yǔ)句
Point p = new Point(10, 10);
object box = p;
p.x = 20;
Console.Write(((Point)box).x);
將在控制臺(tái)上輸出值 10,由于將 p 賦值給 box 是1個(gè)隱式裝箱操作,它將復(fù)制 p 的值。如果將 Point 聲明為 class,由于 p 和 box 將援用同1個(gè)實(shí)例,因此輸出值為 20。
1.3.2 拆箱轉(zhuǎn)換
取消裝箱轉(zhuǎn)換允許將 reference-type 顯式轉(zhuǎn)換為 value-type。存在以下拆箱轉(zhuǎn)換:
請(qǐng)注意,到類型形參的顯式轉(zhuǎn)換將以取消裝箱轉(zhuǎn)換的情勢(shì)履行(如果在運(yùn)行時(shí)它結(jié)束從援用類型到值類型(第 6.2.6 節(jié))的轉(zhuǎn)換)。
對(duì) non-nullable-value-type 取消裝箱的操作包括以下步驟:首先檢查對(duì)象實(shí)例是不是是給定 non-nullable-value-type 的裝箱值,然后將該值從實(shí)例中復(fù)制出來。
對(duì) nullable-type 取消裝箱在源操作數(shù)為 null 時(shí)會(huì)產(chǎn)生 nullable-type 的 null 值;否則將產(chǎn)生從對(duì)象實(shí)例到 nullable-type 的基礎(chǔ)類型的取消裝箱的包裝結(jié)果。
參照前1節(jié)中關(guān)于假想的裝箱類的描寫,從對(duì)象 box 到 value-type T 的取消裝箱轉(zhuǎn)換包括履行表達(dá)式 ((Box)box).value。因此,下面的語(yǔ)句
object box = 123;
int i = (int)box;
在概念上相當(dāng)于
object box = new Box(123);
int i = ((Box)box).value;
為使針對(duì)給定 non-nullable-value-type 的取消裝箱轉(zhuǎn)換在運(yùn)行時(shí)獲得成功,源操作數(shù)的值必須是對(duì)該 non-nullable-value-type 的裝箱值的援用。如果源操作數(shù)為 null,則將引發(fā) System.NullReferenceException。如果源操作數(shù)是對(duì)不兼容對(duì)象的援用,則將引發(fā) System.InvalidCastException。
為使針對(duì)給定 nullable-type 的取消裝箱轉(zhuǎn)換在運(yùn)行時(shí)獲得成功,源操作數(shù)的值必須是 null 或是對(duì)該 nullable-type 的基礎(chǔ) non-nullable-value-type 的裝箱值的援用。如果源操作數(shù)是對(duì)不兼容對(duì)象的援用,則將引發(fā) System.InvalidCastException。
1.4 構(gòu)造類型
泛型類型聲明本身表示未綁定的泛型類型 (unbound generic type),它通過利用類型實(shí)參 (type argument) 被用作構(gòu)成許多不同類型的“藍(lán)圖”。類型實(shí)參編寫在緊跟在泛型類型的名稱后面的尖括號(hào)(< 和 >)中。最少包括1個(gè)類型實(shí)參的類型稱為構(gòu)造類型 (constructed type)。構(gòu)造類型可以在語(yǔ)言中能夠出現(xiàn)類型名的大多數(shù)地方使用。未綁定的泛型類型只能在 typeof-expression(第 7.6.11 節(jié))中使用。
構(gòu)造類型還可以在表達(dá)式中用作簡(jiǎn)單名稱(第 7.6.2 節(jié))或在訪問成員時(shí)使用(第 7.6.4 節(jié))。
在計(jì)算 namespace-or-type-name 時(shí),僅斟酌具有正確數(shù)目的類型形參的泛型類型。因此,可使用同1個(gè)標(biāo)識(shí)符標(biāo)識(shí)不同的類型,條件是那些類型具有不同數(shù)目的類型形參。當(dāng)在同1程序中混合使用泛型和非泛型類時(shí),這是很有用的:
namespace Widgets
{
class Queue {...}
class Queue{...}
}
namespace MyApplication
{
using Widgets;
class X
{
Queue q1; // Non-generic Widgets.Queue
Queueq2; // Generic Widgets.Queue
}
}
即便未直接指定類型形參,type-name 也能夠標(biāo)識(shí)構(gòu)造類型。當(dāng)某個(gè)類型嵌套在泛型類聲明中,并且包括該類型的聲明的實(shí)例類型被隱式用于名稱查找(第 10.3.8.6 節(jié))時(shí),就會(huì)出現(xiàn)這類情況:
class Outer
{
public class Inner {...}
public Inner i; // Type of i is Outer.Inner
}
在不安全代碼中,構(gòu)造類型不能用作 unmanaged-type(第 18.2 節(jié))。
1.4.1 類型實(shí)參
類型實(shí)參列表中的每一個(gè)實(shí)參都只是1個(gè) type。
type-argument-list:
< type-arguments >
type-arguments:
type-argument
type-arguments , type-argument
type-argument:
type
在不安全代碼(第 18 章)中,type-argument 不可以是指針類型。每一個(gè)類型實(shí)參都必須滿足對(duì)應(yīng)的類型形參上的所有束縛(第 10.1.5 節(jié))。
1.4.2 開放和封閉類型
所有類型都可歸類為開放類型 (open type) 或封閉類型 (closed type)。開放類型是包括類型形參的類型。更明確地說:
封閉類型是不屬于開放類型的類型。
在運(yùn)行時(shí),泛型類型聲明中的所有代碼都在1個(gè)封閉構(gòu)造類型的上下文中履行,這個(gè)封閉構(gòu)造類型是通過將類型實(shí)參利用該泛型聲明來創(chuàng)建的。泛型類型中的每一個(gè)類型形參都綁定到特定的運(yùn)行時(shí)類型。所有語(yǔ)句和表達(dá)式的運(yùn)行時(shí)處理都始終使用封閉類型,開放類型僅出現(xiàn)在編譯時(shí)處理進(jìn)程中。
每一個(gè)封閉構(gòu)造類型都有自己的靜態(tài)變量集,任何其他封閉構(gòu)造類型都不會(huì)同享這些變量。由于開放類型在運(yùn)行時(shí)其實(shí)不存在,因此不存在與開放類型關(guān)聯(lián)的靜態(tài)變量。如果兩個(gè)封閉構(gòu)造類型是從相同的未綁定泛型類型構(gòu)造的,并且它們的對(duì)應(yīng)類型實(shí)參屬于相同類型,則這兩個(gè)封閉構(gòu)造類型是相同類型。
1.4.3 綁定和未綁定類型
術(shù)語(yǔ)未綁定類型 (unbound type) 是指非泛型類型或未綁定的泛型類型。術(shù)語(yǔ)綁定類型 (bound type) 是指非泛型類型或構(gòu)造類型。
未綁定類型是指類型聲明所聲明的實(shí)體。未綁定泛型類型本身不是1種類型,不能用作變量、參數(shù)或返回值的類型,也不能用作基類型。可以援用未綁定泛型類型的唯1構(gòu)造是 typeof 表達(dá)式(第 7.6.11 節(jié))。
1.4.4 滿足束縛
每當(dāng)援用構(gòu)造類型或泛型方法時(shí),都會(huì)根據(jù)泛型類型或方法(第 10.1.5 節(jié))上聲明的類型形參束縛對(duì)所提供的類型實(shí)參進(jìn)行檢查。對(duì)每一個(gè) where 子句,將根據(jù)每一個(gè)束縛檢查與命名的類型形參相對(duì)應(yīng)的類型實(shí)參 A,以下所示:
如果給定的類型實(shí)參未滿足1個(gè)或多個(gè)類型形參的束縛,則會(huì)產(chǎn)生編譯時(shí)毛病。
由于類型形參未被繼承,因此束縛也從不被繼承。在下面的示例中,T 需要指定其類型形參 T 上的束縛,以便 T 滿足基類 B所施加的束縛。相反,類 E 不需要指定束縛,由于對(duì)任何 T,List都實(shí)現(xiàn) IEnumerable。
class Bwhere T: IEnumerable {...}
class D: Bwhere T: IEnumerable {...}
class E: B{...}
1.5 類型形參
類型形參是指定形參在運(yùn)行時(shí)要綁定到的值類型或援用類型的標(biāo)識(shí)符。
type-parameter:
identifier
由于類型形參可以使用許多不同的實(shí)際類型實(shí)參進(jìn)行實(shí)例化,因此類型形參具有與其他類型略微不同的操作和限制。這包括:
作為類型,類型形參純潔是1個(gè)編譯時(shí)構(gòu)造。在運(yùn)行時(shí),每一個(gè)類型形參都綁定到1個(gè)運(yùn)行時(shí)類型,運(yùn)行時(shí)類型是通過向泛型類型聲明提供類型實(shí)參來指定的。因此,使用類型形參聲明的變量的類型在運(yùn)行時(shí)將是封閉構(gòu)造類型(第 4.4.2 節(jié))。觸及類型形參的所有語(yǔ)句和表達(dá)式的運(yùn)行時(shí)履行都使用作為該形參的類型實(shí)參提供的實(shí)際類型。
1.6 表達(dá)式樹類型
表達(dá)式樹 (Expression tree) 允許匿名函數(shù)表示為數(shù)據(jù)結(jié)構(gòu)而不是可履行代碼。表達(dá)式樹是 System.Linq.Expressions.Expression情勢(shì)的表達(dá)式樹類型 (expression tree type) 的值,其中 D 是任何拜托類型。對(duì)本規(guī)范的其余部份,我們將使用簡(jiǎn)寫情勢(shì) Expression援用這些類型。
如果存在從匿名函數(shù)到拜托類型 D 的轉(zhuǎn)換,則也存在到表達(dá)式樹類型 Expression的轉(zhuǎn)換。不過,匿名函數(shù)到拜托類型的轉(zhuǎn)換會(huì)生成1個(gè)援用該匿名函數(shù)的可履行代碼的拜托,而到表達(dá)式樹類型的轉(zhuǎn)換則會(huì)創(chuàng)建該匿名函數(shù)的表達(dá)式樹表示情勢(shì)。
表達(dá)式樹是匿名函數(shù)有效的內(nèi)存數(shù)據(jù)表示情勢(shì),它使匿名函數(shù)的結(jié)構(gòu)變得透明和明晰。
與拜托類型 D 1樣,Expression具有與 D 相同的參數(shù)和返回類型。
下面的示例將匿名函數(shù)表示為可履行代碼和表達(dá)式樹。由于存在到 Func
Func
Expression
進(jìn)行上面的賦值以后,拜托 del 援用返回 x + 1 的方法,表達(dá)式目錄樹 exp 援用描寫表達(dá)式 x => x + 1 的數(shù)據(jù)結(jié)構(gòu)。
泛型類型 Expression的準(zhǔn)肯定義和當(dāng)將匿名函數(shù)轉(zhuǎn)換為表達(dá)式樹類型時(shí)用于構(gòu)造表達(dá)式樹的確切規(guī)則不在本規(guī)范的范圍以內(nèi),將另作說明。
有兩個(gè)要點(diǎn)需要明確指出:
Func
調(diào)用此拜托將致使履行表達(dá)式樹所表示的代碼。因此,根據(jù)上面的定義,del 和 del2 等效,而且下面的兩個(gè)語(yǔ)句也將等效:
int i1 = del(1);
int i2 = del2(1);
履行此代碼后,i1 和 i2 的值都為 2。
1.7 dynamic 類型
dynamic 類型在 C# 中具有特殊含義。其用處在于允許進(jìn)行動(dòng)態(tài)綁定(在第 7.2.2 節(jié)中進(jìn)行了詳細(xì)介紹)。
dynamic 被視為與 object 相同,除以下這些方面:
由于此等效性,因此存在以下情況:
dynamic 類型在運(yùn)行時(shí)與 object 沒有區(qū)分。
dynamic 類型的表達(dá)式稱為動(dòng)態(tài)表達(dá)式 (dynamic expression)。
上一篇 負(fù)載均衡的那些算法們
下一篇 VRP系統(tǒng)——4