国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 開發(fā)設(shè)計(jì) > 正文

C#LanguageSpecification5.0(翻譯)第六章轉(zhuǎn)換

2019-11-14 13:59:50
字體:
供稿:網(wǎng)友

C# Language Specification 5.0

轉(zhuǎn)換使表達(dá)式可以當(dāng)做一個(gè)明確的類型來加以處理。轉(zhuǎn)換使得所給定類型的表達(dá)式以不同類型來處理,或使得沒有某個(gè)類型的表達(dá)式獲得該類型。轉(zhuǎn)換可以是顯式或隱式的,而這決定了是否需要顯式地強(qiáng)制轉(zhuǎn)換。比方說,從類型 int 向類型 long 的轉(zhuǎn)換是隱式的,所以 int 類型表達(dá)式可以隱式地當(dāng)做 long 的來處理。反過來轉(zhuǎn)換,從類型 long 轉(zhuǎn)換為 int 是顯式的,需要顯式的強(qiáng)制轉(zhuǎn)換(explicit cast)。

int a = 123;long b = a;      // 從 int 到 long 隱式轉(zhuǎn)換int c = (int) b; // 從 long 到 int 顯式轉(zhuǎn)換

某些轉(zhuǎn)換由語言來定義。程序同樣可以定義它們子集的轉(zhuǎn)換(第六章第四節(jié))。


隱式轉(zhuǎn)換

以下轉(zhuǎn)換被分類為隱式轉(zhuǎn)換:

  • 標(biāo)識(shí)(identity)轉(zhuǎn)換
  • 隱式數(shù)值轉(zhuǎn)換
  • 隱式枚舉轉(zhuǎn)換
  • 隱式可空值轉(zhuǎn)換
  • 空值文本轉(zhuǎn)換
  • 隱式引用轉(zhuǎn)換
  • 裝箱轉(zhuǎn)換
  • 隱式動(dòng)態(tài)(dynamic)轉(zhuǎn)換
  • 隱式常量表達(dá)式轉(zhuǎn)換
  • 用戶定義匿名轉(zhuǎn)換
  • 匿名函數(shù)轉(zhuǎn)換
  • 方法組轉(zhuǎn)換

隱式換磚(implicit conversions)可發(fā)生于多種情況下,包括函數(shù)成員調(diào)用(第七章第 5.4 節(jié))、強(qiáng)值轉(zhuǎn)換表達(dá)式(cast exPRessions,第七章第 7.6 節(jié))以及賦值(第七章第十七節(jié))。

預(yù)定義隱式轉(zhuǎn)換(pre-defined implicit conversions)總是成功的,永不會(huì)導(dǎo)致異常拋出。正確設(shè)計(jì)用戶定義隱式轉(zhuǎn)換(user-defined implicit conversions)也能表現(xiàn)(exhibit)出這些特征(characteristics)。

對(duì)于轉(zhuǎn)換的目的來說,objectdynamic 類型被視為等價(jià)。

然而,動(dòng)態(tài)轉(zhuǎn)換(dynamic conversions,第六章第 1.8 節(jié)以及第六章第 2.6 節(jié))僅適用于類型為 dynamic(第四章第七節(jié))的表達(dá)式。

標(biāo)識(shí)轉(zhuǎn)換

標(biāo)識(shí)轉(zhuǎn)換(identity conversion)可將任意類型轉(zhuǎn)換為其相同類型。這種轉(zhuǎn)換之所以存在,是因?yàn)橐屢咽撬桀愋偷膶?shí)體能被認(rèn)為是可轉(zhuǎn)換(為該類型)的。

由于 objectdynamic 被視為等價(jià),所以在 objectdynamic 之間以及在即將出現(xiàn)的所有 dynamic 轉(zhuǎn)為 object 的轉(zhuǎn)換具有相同構(gòu)造類型的之間,存在一個(gè)標(biāo)識(shí)轉(zhuǎn)換。

隱式數(shù)值轉(zhuǎn)換

隱式數(shù)值轉(zhuǎn)換(implicit numeric conversions)包括:

  • sbyteshortintlongfloatdoubledecimal
  • byteshortushortintuintlongulongfloatdoubledecimal
  • shortintlongfloatdoubledecimal
  • ushortintuintlongulongfloatdoubledecimal
  • intlongfloatdoubledecimal
  • uintlongulongfloatdoubledecimal
  • longfloatdoubledecimal
  • ulongfloatdoubledecimal
  • charushortintuintlongulongfloatdoubledecimal
  • floatdouble

從 int、uint、long 或 ulong 轉(zhuǎn)換為 float,從 long 或 ulong 轉(zhuǎn)換為 double 會(huì)丟失精度(loss of precision),但不會(huì)導(dǎo)致數(shù)量級(jí)的丟失(loss of magnitude)。其它的隱式數(shù)制轉(zhuǎn)換不會(huì)丟失任何信息。

不存在任何向 char 類型的隱式轉(zhuǎn)換,所以任何數(shù)值類型的值都不會(huì)自動(dòng)轉(zhuǎn)換為字符類型。

隱式枚舉轉(zhuǎn)換

隱式枚舉轉(zhuǎn)換允許將 decimal-integer-literal 0 轉(zhuǎn)換為任何枚舉類型(enum-type)和任何基礎(chǔ)類型為枚舉類型(enum-type)的非空值類型(nullable-type)。在后一種情況下,這個(gè)轉(zhuǎn)換工作通過轉(zhuǎn)換為基礎(chǔ)枚舉類型并對(duì)結(jié)果進(jìn)行封裝(第四章第 1.10 節(jié))計(jì)算所得。

隱式可空值轉(zhuǎn)換

對(duì)不可為空值類型(non-nullable value types)的預(yù)定義隱式轉(zhuǎn)換操作也可以用于其可空類型的版本上。對(duì)于每個(gè)從非可空值類型 S 到非可空值類型 T 的預(yù)定義隱式標(biāo)識(shí)和數(shù)值轉(zhuǎn)換,都存在如下可空的隱式轉(zhuǎn)換:

  • S?T? 的隱式轉(zhuǎn)換;
  • ST? 的隱式轉(zhuǎn)換。

基于從 S 到 T 的基礎(chǔ)轉(zhuǎn)換來計(jì)算隱式可空轉(zhuǎn)換的過程如下:

  • 如果可空轉(zhuǎn)換是從 S?T?
    • 如果源值(source value)為空(null,即 HasValue 屬性為 false),則其結(jié)果為 T? 類型的 null 值;
    • 不然,轉(zhuǎn)換計(jì)算的過稱謂將 S? 解包為 S,然后進(jìn)行 ST 的基礎(chǔ)轉(zhuǎn)換,最后將 T 包裝(第四章第 1.10 節(jié))為 T?
  • 如果可空轉(zhuǎn)換是從 ST?,則轉(zhuǎn)換計(jì)算過程為從 ST 的基礎(chǔ)轉(zhuǎn)換,然后從 T 包裝為 T?

空值文本轉(zhuǎn)換

從空值文本(null literal)到可空類型(nullable type)存在隱式轉(zhuǎn)換。這種轉(zhuǎn)換能產(chǎn)生所給定可空類型的空值(null value,第四章第 1.10 節(jié))。

隱式引用轉(zhuǎn)換

隱式引用轉(zhuǎn)換(implicit reference conversions)是:

  • 從任何 reference-typeobjectdynamic
  • 從任何 class-type S 到任何 class-type T(前提是 S 是從 T 派生的);
  • 從任何 class-type S 到任何 interface-type T(前提是 S 是 T 的實(shí)現(xiàn))
  • 從任何 interface-type S 任何 interface-type T(前提是 S 是從 T 派生的);
  • 從元素類型為 SEarray-type S 到元素類型為 TEarray-type T(前提是下列所有條件均為 true):
    • S 和 T 是指元素類型的不同。換句話說,S 和 T 擁有相同維度;
    • SE 和 TE 都是 reference-type
    • 存在從 SE 到 TE 的隱式引用轉(zhuǎn)換。
  • 從任何 array-typeSystem.Array 及其實(shí)現(xiàn)的接口;
  • 從單維度數(shù)組類型 S[]System.Collections.Generic.IList<T> 及其基接口(前提是存在從 S 到 T 的隱式標(biāo)識(shí)或引用轉(zhuǎn)換);
  • 從任何 delegate-typeSystem.Delegate 及其實(shí)現(xiàn)的接口;
  • 從空值文本(null literal)到任何 reference-type
  • 從任何 reference-typereference-type T(前提是其具有到 reference-type T0 的隱式標(biāo)識(shí)或引用轉(zhuǎn)換,且 T0 具有到 T 的標(biāo)識(shí)轉(zhuǎn)換);
  • 從任何 reference-type 到接口或委托類型 T(前提是具有到接口或委托類型 T0 的隱式標(biāo)識(shí)或引用轉(zhuǎn)換,且 T0 為可變化轉(zhuǎn)換(variance-convertible,第十三章第 1.3.2 節(jié))到 T);
  • 涉及到已知引用類型的類型形參的隱式轉(zhuǎn)換。有關(guān)涉及類型形參的隱式轉(zhuǎn)換的信息參見第六章第 1.10 節(jié)。

隱式引用轉(zhuǎn)換是在 reference-type 之間的轉(zhuǎn)換,可以證明這種轉(zhuǎn)換總能成功,故而不需要在「運(yùn)行時(shí)」對(duì)其進(jìn)行檢查。

引用轉(zhuǎn)換,不管是顯式還是隱式,都不會(huì)改變被轉(zhuǎn)換的對(duì)象的引用標(biāo)識(shí)(referential identity)。換而言之,雖然引用轉(zhuǎn)換可以改變引用的類型,但不會(huì)改變所引用的對(duì)象的類型或值

裝箱轉(zhuǎn)換

裝箱轉(zhuǎn)換(boxing conversion)允許值類型隱式轉(zhuǎn)換為引用類型。從任何非可空值類型到 objectdynamicSystem.ValueType 以及 non-nullable-value-type 實(shí)現(xiàn)的任何 interface-type 都存在裝箱轉(zhuǎn)換。此外,enum-type 能被轉(zhuǎn)換為 System.Enum 類型。

存在從可空類型到引用類型的裝箱轉(zhuǎn)換,當(dāng)且僅當(dāng)該可空類型的不可空值類型存在向引用類型裝箱轉(zhuǎn)換。

如果值類型具有到接口類型 I0 的裝箱轉(zhuǎn)換,且 I0 具有到接口類型 I 的標(biāo)識(shí)轉(zhuǎn)換,則值類型具有慈寧到 I 的裝箱轉(zhuǎn)換。

如果值類型具有到接口類型或委托類型 I0 的裝箱轉(zhuǎn)換,且 I0 可變化轉(zhuǎn)換(第十三章第 1.3.2 節(jié))為接口類型 I,則值類型具有到接口類型 I 的裝箱轉(zhuǎn)換。

對(duì)非可空值類型的值的裝箱可以包括以下操作:分配一個(gè)對(duì)象實(shí)例,然后將值類型的值復(fù)制進(jìn)該實(shí)例。結(jié)構(gòu)可裝箱為類型 System.ValueType,因?yàn)樗撬薪Y(jié)構(gòu)的基類型(第十一章第 3.2 節(jié))。

對(duì)非可空類型的值的裝箱按以下步驟進(jìn)行:

  • 如果源值是空(null,即 HasValue 屬性為 false),其結(jié)果是目標(biāo)類型的空引用(null reference);
  • 否則,其結(jié)果為對(duì)源值進(jìn)行解包與裝箱所得的裝箱 T 的引用。

關(guān)于裝箱轉(zhuǎn)換的介紹請(qǐng)閱讀第四章第 3.1 節(jié)。

隱式動(dòng)態(tài)轉(zhuǎn)換

存在從 dynamic 類型表達(dá)式到任何類型 T 的隱式動(dòng)態(tài)轉(zhuǎn)換(implicit dynamic conversion)。轉(zhuǎn)換是動(dòng)態(tài)綁定的(dynamically bound,第七章第 2.2 節(jié)),這意味著在「運(yùn)行時(shí)」能發(fā)現(xiàn)從表達(dá)式的「運(yùn)行時(shí)」類型到類型 T 的隱式轉(zhuǎn)換。如果未發(fā)現(xiàn)任何轉(zhuǎn)換,則拋出「運(yùn)行時(shí)」異常。

注意這種隱式轉(zhuǎn)換似乎違背了第六章第一節(jié)開頭部分的建議,隱式轉(zhuǎn)換不應(yīng)該導(dǎo)致異常。但這不是轉(zhuǎn)換自身導(dǎo)致的異常,而是轉(zhuǎn)換時(shí)動(dòng)詞「發(fā)現(xiàn)」(finding)所導(dǎo)致的異常。「運(yùn)行時(shí)」異常的風(fēng)險(xiǎn)是使用動(dòng)態(tài)綁定所固有的。如果不需要?jiǎng)討B(tài)綁定轉(zhuǎn)化,表達(dá)式首先轉(zhuǎn)換為一個(gè) object,然后轉(zhuǎn)換為所需的類型。

下例說明了隱式動(dòng)態(tài)轉(zhuǎn)換:

object o  = “object”dynamic d = “dynamic”;string s1 = o; // 「編譯時(shí)」失敗,不存在轉(zhuǎn)換string s2 = d; // 編譯且「運(yùn)行時(shí)」成功int i     = d; // 編譯但「運(yùn)行時(shí)」失敗,不存在轉(zhuǎn)換

對(duì) s2i 的賦值都使用了隱式動(dòng)態(tài)轉(zhuǎn)換(implicit dynamic conversions),所綁定的操作會(huì)一直掛起,直到「運(yùn)行時(shí)(run-time)」才執(zhí)行。在「運(yùn)行時(shí)」,可以看到從 d 的「運(yùn)行時(shí)」類型(string)到目標(biāo)類型的隱式轉(zhuǎn)換。轉(zhuǎn)換會(huì)找到 string 而不是 int。

隱式常量表達(dá)式轉(zhuǎn)換

隱式常量表達(dá)式轉(zhuǎn)換(implicit constant expression conversions)允許以下轉(zhuǎn)換:

  • int 類型的 constant-expression(第七章第十九節(jié))能被轉(zhuǎn)換為 sbyte, byte, short, ushort, uintulong,所提供的轉(zhuǎn)換結(jié)果在目標(biāo)類型的合法區(qū)間內(nèi)。
  • long 類型的 constant-expression 能被轉(zhuǎn)換為 ulong,所提供的結(jié)果為非負(fù)數(shù)。

涉及類型形參的隱式轉(zhuǎn)換

對(duì)于給定類型形參 T 存在下列隱式轉(zhuǎn)換:

  • 從 T 轉(zhuǎn)換為對(duì)其有效基類 C,從 T 轉(zhuǎn)換為任意基類 C 以及從 T 轉(zhuǎn)換為任意 C 實(shí)現(xiàn)的接口。若 T 在「運(yùn)行時(shí)」為值類型,則轉(zhuǎn)換執(zhí)行為裝箱轉(zhuǎn)換;相反,若 T 為引用類型,其轉(zhuǎn)換具體執(zhí)行為隱式引用轉(zhuǎn)換或標(biāo)識(shí)轉(zhuǎn)換。
  • 從 T 轉(zhuǎn)換為其有效接口集之一的接口類型 I,以及從 T 轉(zhuǎn)換為任意基接口 I。若 T 在「運(yùn)行時(shí)」為值類型,則轉(zhuǎn)換實(shí)際執(zhí)行為裝箱轉(zhuǎn)換;相反,若 T 為引用類型,則轉(zhuǎn)換實(shí)際執(zhí)行為隱式引用轉(zhuǎn)換或標(biāo)識(shí)轉(zhuǎn)換。
  • 從 T 轉(zhuǎn)換為類型參數(shù) U(假設(shè) T 依賴于 U,第十章第 1.5 節(jié))。若 U 在「運(yùn)行時(shí)」是值類型,則 T 和 U 都必須為同種類型且實(shí)際并無轉(zhuǎn)換發(fā)生;若 T 為值類型,轉(zhuǎn)換實(shí)際執(zhí)行為裝箱轉(zhuǎn)換;再不然,則轉(zhuǎn)換實(shí)際執(zhí)行為隱式引用轉(zhuǎn)換或標(biāo)識(shí)轉(zhuǎn)換。
  • 從空文本(null literal)轉(zhuǎn)換為 T(已知 T 為一個(gè)引用類型)。
  • 從 T 轉(zhuǎn)換為一個(gè)引用類型 I(若 T 具有到引用類型 S0 的隱式轉(zhuǎn)換,以及 S0 具有到 S 的標(biāo)識(shí)轉(zhuǎn)換),那么在「運(yùn)行時(shí)」轉(zhuǎn)換實(shí)際執(zhí)行為到 S0 的相同轉(zhuǎn)換。
  • 從 T 到接口類型 I(若 T 具有到接口類型或委托類型 I0 的隱式轉(zhuǎn)換,且 I0 可變化轉(zhuǎn)換為 I,第十三章第 1.3.2 節(jié))。在運(yùn)行時(shí),如果 T 為值類型,則轉(zhuǎn)換實(shí)際執(zhí)行為裝箱轉(zhuǎn)換;不然,則實(shí)際執(zhí)行為隱式引用轉(zhuǎn)換或標(biāo)識(shí)轉(zhuǎn)換。

如果已知 T 是引用類型(第十章第 1.5 節(jié)),上述轉(zhuǎn)換都被劃歸為隱式引用轉(zhuǎn)換(第六章第 1.6 節(jié))。如果不知道是不是引用類型,則劃歸為裝箱轉(zhuǎn)換(boxing conversions,第六章第 1.7 節(jié)),

用戶定義隱式轉(zhuǎn)換

用戶定義隱式轉(zhuǎn)換(user-defined implicit conversion)由可選的標(biāo)準(zhǔn)隱式轉(zhuǎn)換(optional standard implicit conversion)、執(zhí)行用戶定義隱式轉(zhuǎn)換的操作符以及另一個(gè)可選的基礎(chǔ)隱式轉(zhuǎn)換等三部分組成。運(yùn)行用戶定義隱式轉(zhuǎn)換的確切規(guī)則詳見第六章第 4.4 節(jié)。

匿名函數(shù)轉(zhuǎn)換與方法組轉(zhuǎn)換

匿名函數(shù)(anonymous function)與方法組(method groups)自身不具有類型(do not have types in and of themselves),然可隱式轉(zhuǎn)換為委托類型或表達(dá)式樹類型。匿名函數(shù)的轉(zhuǎn)換詳見第六章第五節(jié),方法組轉(zhuǎn)換詳見第六章第六節(jié)。


顯式轉(zhuǎn)換

下列轉(zhuǎn)換被劃歸為顯式轉(zhuǎn)換(explicit conversions):

  • 所有的隱式轉(zhuǎn)換
  • 顯式數(shù)值轉(zhuǎn)換
  • 顯式枚舉轉(zhuǎn)換
  • 顯式可空值轉(zhuǎn)換
  • 顯式引用轉(zhuǎn)換
  • 顯示接口轉(zhuǎn)換
  • 拆箱轉(zhuǎn)換
  • 顯式動(dòng)態(tài)轉(zhuǎn)換
  • 用戶定義顯式轉(zhuǎn)換

顯式轉(zhuǎn)換可出現(xiàn)在強(qiáng)制轉(zhuǎn)換表達(dá)式(cast expressions,第七章第 7.6 節(jié))內(nèi)。

顯式轉(zhuǎn)換集包括所有隱式轉(zhuǎn)換,這意味著允許使用冗余的強(qiáng)制轉(zhuǎn)換表達(dá)式(redundant cast expressions)。

顯式轉(zhuǎn)換(排除隱式轉(zhuǎn)換的那部分)不能保證總能轉(zhuǎn)換成功,轉(zhuǎn)換可能會(huì)丟失信息,并且轉(zhuǎn)換前后類型顯著不同(sufficiently different)。

顯式數(shù)值轉(zhuǎn)換

顯示數(shù)值轉(zhuǎn)換(explicit numeric conversions)是指從 numeric-type 到另一個(gè) numeric-type 的轉(zhuǎn)換,這種轉(zhuǎn)換不能用已知的隱式數(shù)值轉(zhuǎn)換(第六章第 1.2 節(jié))來實(shí)現(xiàn),它包括:

  • 從 sbyte 到 byte, ushort, uint, ulong 以及 char
  • 從 byte 到 sbyte and char
  • 從 short 到 sbyte, byte, ushort, uint, ulong 以及 char
  • 從 ushort 到 sbyte, byte, short, or char
  • 從 int 到 sbyte, byte, short, ushort, uint, ulong 以及 char
  • 從 uint 到 sbyte, byte, short, ushort, int 以及 char
  • 從 long 到 sbyte, byte, short, ushort, int, uint, ulong 以及 char
  • 從 ulong 到 sbyte, byte, short, ushort, int, uint, long 以及 char
  • 從 char 到 sbyte, byte 以及 short
  • 從 float 到 sbyte, byte, short, ushort, int, uint, long, ulong, char 以及 decimal
  • 從 double 到 sbyte, byte, short, ushort, int, uint, long, ulong, char, float 以及 decimal
  • 從 decimal 到 sbyte, byte, short, ushort, int, uint, long, ulong, char, float 以及 double

由于顯式轉(zhuǎn)換包括所有隱式轉(zhuǎn)換和顯式數(shù)值轉(zhuǎn)換,所以其總可以使用強(qiáng)制轉(zhuǎn)換表達(dá)式(cast expression,第七章第 7.6 節(jié))將任意 numeric-type 轉(zhuǎn)換為其它任意 numeric-type

顯式數(shù)值轉(zhuǎn)換可能會(huì)丟失信息或?qū)е庐惓伋觥o@式數(shù)值轉(zhuǎn)換的處理過程如下:

  • 對(duì)于從一個(gè)整型轉(zhuǎn)換為另一個(gè)整型,其處理取決于轉(zhuǎn)換發(fā)生時(shí)的溢出檢查上下文(overflow checking context,第七章第 6.12 節(jié)):
    • 在 checked 上下文中,如果源操作數(shù)的值在目標(biāo)類型的區(qū)間內(nèi),轉(zhuǎn)換成功;但如果超出了目標(biāo)類型的合法區(qū)間,則會(huì)拋出 System.OverflowException 異常。
    • 在 unchecked 上下文中,轉(zhuǎn)換總會(huì)成功,其操作過程為:
      • 如果源類型大于目標(biāo)類型,則源值通過丟棄精度使其截?cái)唷H缓髮⑵浣Y(jié)果視作目標(biāo)類型的值。
      • 如果源類型小于目標(biāo)類型,則源值或按符號(hào)擴(kuò)展(sign-extended)或按零值擴(kuò)展(zero-extended),使其大小與目標(biāo)值相同。符號(hào)擴(kuò)展用于有符號(hào)的源類型,零值擴(kuò)展用于無符號(hào)的源類型。然后將其結(jié)果視作目標(biāo)類型的值。
      • 如果源類型的大小與目標(biāo)類型相同,那么源值將視為目標(biāo)類型的值。
  • 對(duì)于從 decimal 到整型的轉(zhuǎn)換,源值向零舍入(rounded towards zero)到最接近的整數(shù)值,而這個(gè)值即為轉(zhuǎn)換的結(jié)果。如果所得的整數(shù)值超出目標(biāo)類型的范圍,則會(huì)拋出 System.OverflowException 異常。
  • 對(duì)于從 float 或 double 到整型的轉(zhuǎn)換,其處理過程取決于執(zhí)行轉(zhuǎn)換過程中的溢出檢查上下文(overflow checking context,第七章第 6.12 節(jié)):
    • 在 checked 上下文中,轉(zhuǎn)換處理的過程如下:
      • 如果操作數(shù)的值是 NaN 或無限大(infinite),那么會(huì)拋出 System.OverflowException 異常。
      • 否則,源操作數(shù)會(huì)向零舍入到最接近的整數(shù)值。如果整數(shù)值在目標(biāo)類型的范圍內(nèi),則將其作為轉(zhuǎn)換之結(jié)果。
      • 不然,則拋出 System.OverflowException 異常。
    • 在 unchecked 上下文,無論如何轉(zhuǎn)換均會(huì)成功,其處理過程如下:
      • 如果操作數(shù)是 NaN 或無限大(infinite),則轉(zhuǎn)換結(jié)果為目標(biāo)類型的一個(gè)未經(jīng)指定的值(unspecified value)。
      • 否則,源操作數(shù)向零舍入到最接近的整型數(shù)。如果整數(shù)值位于目標(biāo)類型的范圍內(nèi),則將其作為轉(zhuǎn)換之結(jié)果予以返回。
      • 不然,返回一個(gè)目標(biāo)類型的未經(jīng)指定的值。
  • 對(duì)于從 double 到 float 的轉(zhuǎn)換,double 值將舍入到最接近的 float 值。如果 double 值太小,無法表示為 float,則結(jié)果為正零(positive zero)或負(fù)零(negative zero)。如果 double 值過大,無法表示為 float 值,則其結(jié)果為正無窮大(positive infinity)或負(fù)無窮大(negative infinity)。如果 double 是 NaN,則結(jié)果也返回 NaN。
  • 對(duì)于從 float 或 double 到 decimal 的轉(zhuǎn)換,源值將轉(zhuǎn)換為 decimal 表示形式,并在需要時(shí)將其第二十八位小數(shù)向上舍入(第四章第 1.7 節(jié))到最接近的數(shù)字。如果源值太小,無法表示為 decimal,則結(jié)果變?yōu)榱恪H绻粗凳?NaN、無限大或太大而無法表示為 decimal,則拋出 System.OverflowException 異常。
  • 對(duì)于從 decimal 到 float 或 double 的轉(zhuǎn)換,decimal 的值將舍入為最接近 double 或 float 的值。雖然這種轉(zhuǎn)換會(huì)丟失精度,但從不會(huì)拋出異常。

顯式枚舉轉(zhuǎn)換

顯式枚舉轉(zhuǎn)換(explicit enumeration conversions)是:

  • sbyte, byte, short, ushort, int, uint, long, ulong, char, float, doubledecimal 到任何 enum-type
  • 從任何 enum-typesbyte, byte, short, ushort, int, uint, long, ulong, char, float, doubledecimal
  • 從任何 enum-type 到任何其它 enum-type

在兩個(gè)類型之間進(jìn)行顯式枚舉轉(zhuǎn)換是通過處理任何參與的 enum-type 都按該 enum-type 的基礎(chǔ)類型處理,然后在產(chǎn)生的類型之間使用顯式或隱式的數(shù)值轉(zhuǎn)換。比方說,給定一個(gè) enum-type E,其基礎(chǔ)類型為 int,從 E 到 byte 的轉(zhuǎn)換會(huì)按從 int 到 byte 的顯式數(shù)值轉(zhuǎn)換(第六章第 2.1 節(jié))來處理,而從 byte 到 E 的轉(zhuǎn)換則會(huì)按從 byte 到 int 的隱式數(shù)值轉(zhuǎn)換(第六章第 1.2 節(jié))來處理。

顯式可空值轉(zhuǎn)換

顯式可空值轉(zhuǎn)換(explicit nullable conversions)允許對(duì)不可空值類型(non-nullable value types)及其可空值形式的類型執(zhí)行預(yù)定義顯式轉(zhuǎn)換。對(duì)于每個(gè)從不可空值類型 S 到不可空值類型 T 的預(yù)定義顯式轉(zhuǎn)換(predefined explicit conversions)(第六章第 1.1 節(jié),1.2 節(jié),1.3 節(jié),2.1 節(jié)以及 2.2 節(jié)),存在下列可空轉(zhuǎn)換:

  • S?T? 的顯式轉(zhuǎn)換。
  • ST? 的顯式轉(zhuǎn)換。
  • S?T 的顯式轉(zhuǎn)換。

基于從 ST 的基礎(chǔ)轉(zhuǎn)換的可空轉(zhuǎn)換運(yùn)算步驟如下:

  • 如果是從 S?T? 的可空轉(zhuǎn)換:
    • 如果源值是空(null,即 HasValue 屬性為 false),其結(jié)果為類型 T? 的 null 值。
    • 不然的話,轉(zhuǎn)換計(jì)算過程是從 S?S 的解包,然后進(jìn)行從 ST 的基礎(chǔ)轉(zhuǎn)換,最后從 T 包裝為 T?
  • 如果是從 ST? 的可空轉(zhuǎn)換,那么轉(zhuǎn)換的運(yùn)算過程是將 S 基礎(chǔ)轉(zhuǎn)換為 T,然后將 T 包裝為 T?
  • 如果是從 S?T 的可空轉(zhuǎn)換,那么轉(zhuǎn)換的運(yùn)算過程是將 S? 解包為 S,然后從 S 基礎(chǔ)轉(zhuǎn)換為 T

注意,如果對(duì)一個(gè)為 null 值的可空值進(jìn)行解包會(huì)引發(fā)異常。

顯式引用轉(zhuǎn)換

顯式引用轉(zhuǎn)換(explicit reference conversions)是:

  • objectdynamic 到任何其它 reference-type
  • 從任何 class-type S 到任何 class-type T(前提是 S 為 T 的基類)
  • 從任何 class-type S 到任何 interface-type T(前提是 S 不是密封的(sealed)且 S 沒有實(shí)現(xiàn) T)
  • 從任何 interface-type S 到任何 class-type T(前提是 T 不是密封的或 T 實(shí)現(xiàn)了 S)
  • 從任何 interface-type S 到任何 interface-type T(前提是 S 不是派生自 T 的)
  • 從元素類型為 SEarray-type S 到元素類型為 TE 的 `array-type T(前提是以下條件均為 true):
    • S 和 T 的不同僅在于元素類型,換句話說 S 和 T 擁有相同數(shù)量的維度;
    • SE 和 TE 都是引用類型;
    • 存在從 SE 到 TE 的顯示引用轉(zhuǎn)換。
  • System.Array 和它所實(shí)現(xiàn)的接口道任何 array-type
  • 從單維度數(shù)組類型 S[]System.Collections.Generic.IList<T> 及其基接口(前提是具有從 S 到 T 的顯式引用轉(zhuǎn)換)
  • System.Collections.Generic.IList<S> 及其基接口到單維度數(shù)組類型 T[](前提是具有從 S 到 T 的顯示標(biāo)識(shí)或引用轉(zhuǎn)換)
  • System.Delegate 及其所實(shí)現(xiàn)的接口道任何 delegate-type
  • 從一個(gè)引用類型到另一個(gè)引用類型 T(前提是它具有到引用類型 T0 的顯式引用轉(zhuǎn)換且 T0 具有到 T 的標(biāo)識(shí)轉(zhuǎn)換)
  • 從一個(gè)引用類型到一個(gè)接口或委托類型 T(前提是它具有到接口或委托類型 T0 的顯式引用轉(zhuǎn)換,且 T0 具有可變轉(zhuǎn)換為 T 或 T 可變轉(zhuǎn)換為 T0(第十三章第 1.3.2 節(jié)))
  • 從 D<S1…Sn> 到 D<T1…Tn>,其中 D<X1…Xn> 是泛型委托類型(generic delegate type), D<S1…Sn> 與 D<T1...Tn> 是不兼容(not compatible with)或不相容(not identical to)的,且對(duì)于 D 的每個(gè)類型形參 Xi,均存在以下情況:
    • 如果 Xi 是固定的,那么 Si 與 Ti 同。
    • 如果 Xi 是協(xié)變(covariant)的,那么具有從 Si 到 Ti 的隱式或顯式的標(biāo)識(shí)或引用轉(zhuǎn)換。
    • 如果 Xi 是逆變(contravariant)的, 那么 Si 與 Ti 同或同為引用類型。
  • 涉及已知引用類型的類型形參的顯式轉(zhuǎn)換。有關(guān)類型形參的顯式轉(zhuǎn)換的更多信息查看第六章第 2.7 節(jié)。

顯式引用轉(zhuǎn)換是需要在「運(yùn)行時(shí)」檢查其正確的 reference-type 之間進(jìn)行的轉(zhuǎn)換。

為了顯式引用轉(zhuǎn)換在「運(yùn)行時(shí)」成功,源操作數(shù)的值必須為空(null),或源操作數(shù)所引用對(duì)象的實(shí)際類型必須是一個(gè)能通過隱式引用轉(zhuǎn)換(第六章第 1.6 節(jié))或裝箱轉(zhuǎn)換(第六章第 1.7 節(jié))轉(zhuǎn)換為目標(biāo)類型的類型。如果顯式引用轉(zhuǎn)換失敗,會(huì)拋出 System.InvalidCastException 異常。

引用轉(zhuǎn)換,無論是顯示還是隱式,都不會(huì)改變被轉(zhuǎn)換對(duì)象的引用標(biāo)識(shí)(referential identity)。換句話說,雖然引用轉(zhuǎn)換可以改變所引用的類型,但從不會(huì)改變所引用對(duì)象的類型或值。

拆箱轉(zhuǎn)換

拆箱轉(zhuǎn)換(unboxing conversion)引用類型顯式轉(zhuǎn)換為值類型。存在從類型 objectdynamicSystem.ValueType 到任何 non-nullable-value-type 以及從任何 interface-type 到任何實(shí)現(xiàn) interface-typenon-nullable-value-type 的拆箱轉(zhuǎn)換。而且,類型 System.Enum 可以拆箱為任何 enum-type

存在從引用類型到 nullable-type 的拆箱轉(zhuǎn)換,前提是存在從該引用類型到 nullable-type 的基礎(chǔ)類型 non-nullable-value-type 的拆箱轉(zhuǎn)換。

如果值類型 S 具有從接口類型 I 的拆箱轉(zhuǎn)換,且 I0 具有從接口類型到 I 的標(biāo)識(shí)轉(zhuǎn)換,則其具有來自 I 的拆箱轉(zhuǎn)換。

如果值類型 S 具有來自接口類型或委托類型 I0 的拆箱轉(zhuǎn)換,且 I0 可變化轉(zhuǎn)換為 I 或 I 可變換轉(zhuǎn)換為 I0(第十三章第 1.3.2 節(jié)),則其具有來自 I 的拆箱轉(zhuǎn)換。

拆箱操作包括以下步驟:一是檢查對(duì)象實(shí)例是否為給定值類型的裝箱值,二是將該值復(fù)制出該實(shí)例。對(duì)一個(gè) nullable-type 類型的 null 引用將產(chǎn)生該類型的 null 值。結(jié)構(gòu)可以從類型 System.ValueType 拆箱,因?yàn)樵擃愋褪撬薪Y(jié)構(gòu)的基類(第十一章第 3.2 節(jié))。

更多拆箱轉(zhuǎn)換的介紹可查看第四章第 3.2 節(jié)。

顯式動(dòng)態(tài)轉(zhuǎn)換

存在從 dynamic 到任意類型 T 的顯式動(dòng)態(tài)轉(zhuǎn)換(explicit dynamic conversion)。轉(zhuǎn)換時(shí)動(dòng)態(tài)綁定(第七章第 2.2 節(jié))的,這意味著在「運(yùn)行時(shí)」時(shí),可以看到從表達(dá)式的「運(yùn)行時(shí)」類型到 T 的顯式轉(zhuǎn)換。如果沒有轉(zhuǎn)換發(fā)生,那么將拋出「運(yùn)行時(shí)」異常。

如果轉(zhuǎn)換不需要?jiǎng)討B(tài)綁定,表達(dá)式可以先轉(zhuǎn)換為 object,然后轉(zhuǎn)為所需類型。

如下例所定義:

class C{    int i;    public C(int i) { this.i = i; }    public static explicit Operator C(string s)     {        return new C(int.Parse(s));    }}

下例列舉了顯式動(dòng)態(tài)轉(zhuǎn)換:

object o  = "1";dynamic d = "2";var c1 = (C)o; // 編譯,但顯式引用轉(zhuǎn)換失敗var c2 = (C)d; // 編譯,用戶定義轉(zhuǎn)換成功

oC 的最佳轉(zhuǎn)換發(fā)生于「編譯時(shí)」的顯式引用轉(zhuǎn)換。這在「運(yùn)行時(shí)」失敗,是因?yàn)?1 實(shí)際上不是 C。然而,從 dC 的轉(zhuǎn)換作為顯式動(dòng)態(tài)轉(zhuǎn)換(explicit dynamic conversion),在「運(yùn)行時(shí)」之初一直被掛起,從 d 的「運(yùn)行時(shí)」類型——string——到 c 的用戶定義轉(zhuǎn)換出現(xiàn)并成功。

涉及類型形參的顯式轉(zhuǎn)換

對(duì)于給定的類型形參 T 存在下列顯式轉(zhuǎn)換:

  • 從 T 的有效基類 C 轉(zhuǎn)換到 T,以及從 C 的任何基類轉(zhuǎn)換到 T。在「運(yùn)行時(shí)」,如果 T 為值類型,其轉(zhuǎn)換將執(zhí)行以拆箱轉(zhuǎn)換。不然,其轉(zhuǎn)換將執(zhí)行以顯式引用轉(zhuǎn)換或標(biāo)識(shí)轉(zhuǎn)換。
  • 從任何接口類型到 T。在「運(yùn)行時(shí)」,如果 T 為值類型,其轉(zhuǎn)換將執(zhí)行以拆箱轉(zhuǎn)換。不然,其轉(zhuǎn)換將執(zhí)行以顯式引用轉(zhuǎn)換或標(biāo)識(shí)轉(zhuǎn)換。
  • 從 T 轉(zhuǎn)換為任意 interface-type I(前提條件是目前尚未存在從 T 到 I 的隱式轉(zhuǎn)換)。在「運(yùn)行時(shí)」,如果 T 為值類型,則其轉(zhuǎn)換將執(zhí)行以先裝箱轉(zhuǎn)換、爾后顯式引用轉(zhuǎn)換。不然,其轉(zhuǎn)換將執(zhí)行以顯式引用轉(zhuǎn)換或標(biāo)識(shí)轉(zhuǎn)換。
  • 從類型形參 U 到 T(前提是 T 依賴于 U(第十章第 1.5 節(jié)))。在「運(yùn)行時(shí)」,如果 U 為值類型,則 T 和 U 都必須為相同類型且不執(zhí)行任何實(shí)際轉(zhuǎn)換。不然,如果 T 為值類型,轉(zhuǎn)換將執(zhí)行以拆箱轉(zhuǎn)換。再不然,將執(zhí)行以顯式引用轉(zhuǎn)換或標(biāo)識(shí)轉(zhuǎn)換。

如果已知 T 為引用類型,則上述轉(zhuǎn)換將盡數(shù)歸類為顯式引用轉(zhuǎn)換(第六章第 2.4 節(jié))。如果已知 T 不是引用類型,則上述轉(zhuǎn)換盡數(shù)歸類為拆箱轉(zhuǎn)換(第六章第 2.5 節(jié))。

上述規(guī)則不允許從未受約束的類型形參直接顯式轉(zhuǎn)換為非接口類型(non-interface type),其目的是為了避免混淆,并使轉(zhuǎn)換語義清晰。如下例所聲明:

class X<T>{    public static long F(T t) {        return (long)t;   // Error     }}

如果允許從 t 直接轉(zhuǎn)換為 int,極有可能會(huì)認(rèn)為 x<int>.F(7) 將返回 7L。然而并不是如此,因?yàn)閮H當(dāng)綁定時(shí)(binding-time)已知類型為數(shù)字類型時(shí),才會(huì)考慮標(biāo)準(zhǔn)的數(shù)字轉(zhuǎn)換(standard numeric conversions)。為了語義清晰,上例必須這樣寫:

class X<T>{    public static long F(T t) {        return (long)(object)t;  // Ok, but will only work when T is long    }}

這段代碼現(xiàn)在能夠編譯,但當(dāng)「運(yùn)行時(shí)」執(zhí)行 X<int>.F(7) 時(shí)會(huì)拋出異常,這是因?yàn)椴荒軐⒁蜒b箱的 int 直接轉(zhuǎn)換為 long

用戶定義顯式轉(zhuǎn)換

用戶定義顯式轉(zhuǎn)換(user-defined explicit conversion)包括以下三部分:可選的標(biāo)準(zhǔn)顯式轉(zhuǎn)換、執(zhí)行用戶定義的隱式或顯式轉(zhuǎn)換操作、另一個(gè)可選的基礎(chǔ)顯式轉(zhuǎn)換。關(guān)于計(jì)算用戶定義顯式轉(zhuǎn)換的確切規(guī)則詳見第六章第 4.5 節(jié)。


標(biāo)準(zhǔn)轉(zhuǎn)換

標(biāo)準(zhǔn)轉(zhuǎn)換(standard conversions)是作為用戶定義轉(zhuǎn)換(user-defined conversion)的一部分出現(xiàn)的預(yù)定義轉(zhuǎn)換(pre-defined conversions)

標(biāo)準(zhǔn)隱式轉(zhuǎn)換

下列隱式轉(zhuǎn)換被劃入標(biāo)準(zhǔn)隱式轉(zhuǎn)換(standard implicit conversions):

  • 標(biāo)識(shí)轉(zhuǎn)換(第六章第 1.1 節(jié))
  • 隱式數(shù)值轉(zhuǎn)換(第六章第 1.2 節(jié))
  • 隱式可空轉(zhuǎn)換(第六章第 1.4 節(jié))
  • 隱式引用轉(zhuǎn)換(第六章第 1.6 節(jié))
  • 裝箱轉(zhuǎn)換(第六章第 1.7 節(jié))
  • 隱式常量表達(dá)式轉(zhuǎn)換(第六章第 1.8 節(jié))
  • 關(guān)于類型形參的隱式轉(zhuǎn)換(第六章第 1.10 節(jié))

基礎(chǔ)隱式轉(zhuǎn)換不包括用戶定義隱式轉(zhuǎn)換(user-defined implicit conversions)。

標(biāo)準(zhǔn)顯式轉(zhuǎn)換

標(biāo)準(zhǔn)顯式轉(zhuǎn)換(standard explicit conversions)包括所有基礎(chǔ)隱式轉(zhuǎn)換,以及由那些與已知的標(biāo)準(zhǔn)隱式轉(zhuǎn)換反向的轉(zhuǎn)換的子集(subset)所組成。換句話說,如果標(biāo)準(zhǔn)隱式轉(zhuǎn)換存在從 A 到 B 的轉(zhuǎn)換,那么標(biāo)準(zhǔn)顯示轉(zhuǎn)換就存在了從 A 到 B 以及從 B 到 A 的轉(zhuǎn)換。


用戶定義轉(zhuǎn)換

C# 允許由用戶定義轉(zhuǎn)換(user-defined conversions)所擴(kuò)展的預(yù)定義隱式與顯示轉(zhuǎn)換。用戶定義轉(zhuǎn)換是通過在類類型與結(jié)構(gòu)類型中聲明轉(zhuǎn)換運(yùn)算符(conversion operators,第十章第 10.3 節(jié))而引入的。

許可的用戶定義轉(zhuǎn)換

C# 只允許某些用戶定義轉(zhuǎn)換的聲明。具體來說,不可重定義已存在的隱式或顯式轉(zhuǎn)換。

對(duì)于給定源類型 S 和目標(biāo)類型 T,如果 S 或 T 均為可空類型,設(shè) S0 及 T0 引用其基礎(chǔ)類型,否則 S0 及 T0 分別等于 S 與 T。只有當(dāng)以下條件為真時(shí),類型或結(jié)構(gòu)允許聲明從源類型 S 到目標(biāo)類型 T 的轉(zhuǎn)換:

  • S0 與 T0 是不同類型。
  • S0 或 T0 中有一個(gè)是聲明了該運(yùn)算符的類或結(jié)構(gòu)。
  • S0 與 T0 都不是 interface-type
  • 除用戶定義轉(zhuǎn)換外,不存在從 S 到 T 或從 T 到 S 的轉(zhuǎn)換。

適用于用戶定義轉(zhuǎn)換的限制將在第十章第 10.3 節(jié)中進(jìn)一步討論。

提升轉(zhuǎn)換操作符

給定一個(gè)從 non-nullable 值類型 S 到 non-nullable 值類型 T 的用戶定義轉(zhuǎn)換運(yùn)算符,存在從 S? 到 T? 的提升轉(zhuǎn)換操作符(lifted conversion operator)。提升轉(zhuǎn)換操作符執(zhí)行從 S? 到 S 的解包、然后是從 S 到 T 的用戶定義轉(zhuǎn)換,接著是從 T 到 T? 的包裝(null 值 S? 直接轉(zhuǎn)換為 null 值 T? 的除外)。

提升轉(zhuǎn)換操作符與其基礎(chǔ)用戶定義轉(zhuǎn)換運(yùn)算符具有相同的顯式或隱式類別。術(shù)語「用戶定義轉(zhuǎn)換(user-defined conversion)」適用于用戶定義轉(zhuǎn)換運(yùn)算符與提升轉(zhuǎn)換操作符的使用。

用戶定義轉(zhuǎn)換的計(jì)算

用戶定義轉(zhuǎn)換將一個(gè)值從其所屬之類型(即「源類型 source type」)e 轉(zhuǎn)換為另一個(gè)類型(即「目標(biāo)類型 target type」)。用戶定義轉(zhuǎn)換的運(yùn)算集中于查找符合特定源類型與目標(biāo)類型的最精確的用戶定義轉(zhuǎn)換符。次確定過程分為以下部分:

  • 從能被考慮的用戶定義轉(zhuǎn)換操作符的類或結(jié)構(gòu)集中查找。此集由源類型及其基類與目標(biāo)類型及其基類構(gòu)成(隱式假定只有類和結(jié)構(gòu)能被聲明用戶定義操作符,且非類類型不具有基類)。為了執(zhí)行本步驟,如果源類型或目標(biāo)類型為 nullable-type,則改為使用其基礎(chǔ)類型。
  • 透過該類型集,確定哪一個(gè)用戶定義轉(zhuǎn)換操作符和提升轉(zhuǎn)換操作符可被使用。對(duì)于可用的轉(zhuǎn)換操作符而言,其必須可以執(zhí)行標(biāo)準(zhǔn)轉(zhuǎn)換(第六章第三節(jié))來使源類型可被轉(zhuǎn)換為該運(yùn)算符操作數(shù)所要求的類型,且必須可以執(zhí)行標(biāo)準(zhǔn)轉(zhuǎn)換來使運(yùn)算符的結(jié)果類型轉(zhuǎn)換為目標(biāo)類型。
  • 通過可用的用戶定義操作符集,確定哪一個(gè)操作符是最為精準(zhǔn)的。總的來說,最精準(zhǔn)的操作符是操作數(shù)類型「最接近」源類型且結(jié)果類型「最接近」目標(biāo)類型的運(yùn)算符。用戶定義轉(zhuǎn)換操作符優(yōu)先級(jí)高于提升轉(zhuǎn)換操作符。隨后章節(jié)定義了最精準(zhǔn)用戶定義轉(zhuǎn)換運(yùn)算符的確切規(guī)則。

當(dāng)最精準(zhǔn)用戶定義轉(zhuǎn)換操作符被明確了之后,確切的執(zhí)行步驟分為三步:

  • 首先,如果需要,執(zhí)行標(biāo)準(zhǔn)轉(zhuǎn)換,將源類型轉(zhuǎn)換為用戶定義轉(zhuǎn)換運(yùn)算符或提升轉(zhuǎn)換運(yùn)算符的操作數(shù)所要求的類型;
  • 第二步,調(diào)用用戶定義或提升轉(zhuǎn)換操作符執(zhí)行轉(zhuǎn)換;
  • 最后,如果需要,執(zhí)行基礎(chǔ)轉(zhuǎn)換,將用戶定義或提升轉(zhuǎn)換操作符的結(jié)果轉(zhuǎn)換為目標(biāo)類型。

用戶定義轉(zhuǎn)換的執(zhí)行不會(huì)調(diào)用另一個(gè)用戶定義轉(zhuǎn)換或提升轉(zhuǎn)換操作符。換句話來說,從類型 S 到類型 T 的轉(zhuǎn)換將不會(huì)首選調(diào)用從 S 到 X 的用戶定義轉(zhuǎn)換,而后再調(diào)用從類型 X 到類型 T 的用戶定義轉(zhuǎn)換。

用戶定義轉(zhuǎn)換或顯式轉(zhuǎn)換的確切定義將在后述章節(jié)給出。這些定義使用以下術(shù)語:

  • 如果基礎(chǔ)隱式轉(zhuǎn)換(第六章第 3.1 節(jié))存在從類型 A 轉(zhuǎn)換為類型 B,并且 A 和 B 都不是 interface-types,那么我們可以說 A 被 B 包含(be encompassed by),或者說 B 包含 A(encompass)。
  • 類型集包含程度最大的類型是該集中包含所有其它類型的類型。如果不存在單個(gè)包含其他所有類型的類型,那么該集不存在包含最大程度的類型。簡(jiǎn)而言之,包含程度最大的類型是該集中「最大」的類型,每個(gè)其它類型都可隱式轉(zhuǎn)為該類型。
  • 類型集中包含程度最大的類型是指其被該類型集中所有其它類型所包含。假若沒有單個(gè)類型被其它所有類型所包含,那么該集不存在包含程度最大的類型。簡(jiǎn)而言之,被包含程序最大的類型是該集中的「最小」的類型,該類型可以隱式轉(zhuǎn)為其它每一個(gè)類型。

用戶定義隱式轉(zhuǎn)換

從類型 S 到類型 T 的用戶定義隱式轉(zhuǎn)換(user-defined implicit conversion)的處理過程如下:

  • 確定類型 S0 與 T0。如果 S 或 T 是可空類型,則 S0 和 T0 為其基礎(chǔ)類型,否則 S0 與 T0 分別等于 S 和 T。
  • 查找類型集 D,從該類型集中考慮用戶定義轉(zhuǎn)換操作符。此集由 S0(若 S0 為類或結(jié)構(gòu))、S0 的基類(若 S0 為類)以及 T0(若 T0 為類或結(jié)構(gòu))組成。
  • 查找適用的用戶定義轉(zhuǎn)換操作符與提升操作符集 U,U 由用戶定義隱式轉(zhuǎn)換符與提升隱式轉(zhuǎn)換操作符所組成,這些操作符是在 D 中的類或結(jié)構(gòu)內(nèi)聲明的,用于從包含 S 的類型轉(zhuǎn)換為被 T 包含的類型。如果 U 為空(empty),那么轉(zhuǎn)換未定義(undefined)并出現(xiàn)「編譯時(shí)」錯(cuò)誤。
  • 從 U 中查找最精準(zhǔn)的源類型 SX
    • 如果 U 內(nèi)任何運(yùn)算符均從 S 轉(zhuǎn)換,則 SX 為 S。
    • 否則,SX 在 U 中運(yùn)算符的合并源類型集中時(shí)包含程度最大的類型。如果無法找到確切的那個(gè)最大包含類型,那么轉(zhuǎn)換將是不明確的,并伴有「編譯時(shí)」錯(cuò)誤發(fā)生。
  • 從 U 中查找運(yùn)算符的最精準(zhǔn)的目標(biāo)類型 TX
    • 如果 U 內(nèi)的任意轉(zhuǎn)換符均能轉(zhuǎn)換為 T,那么 TX 為 T。
    • 不然, TX 為 U 中運(yùn)算符的合并目標(biāo)類型集中最大包含程度類型。如果無法發(fā)現(xiàn)最大包含程度的類型,則轉(zhuǎn)換是不明確的,并伴有「編譯時(shí)」錯(cuò)誤發(fā)生。
  • 查找最具體的轉(zhuǎn)換運(yùn)算符:
    • 如果 U 只包含一個(gè)從 SX 到 TX 的用戶定義轉(zhuǎn)換操作符,那么這就是最精準(zhǔn)的轉(zhuǎn)換操作符。
    • 不然,如果 U 恰好包含一個(gè)從 SX 到 TX 的提升轉(zhuǎn)換操作符,則這就是最具體的轉(zhuǎn)換操作符。
    • 否則,轉(zhuǎn)換是不明確的,并伴有「編譯時(shí)」錯(cuò)誤發(fā)生。
  • 最后,應(yīng)用轉(zhuǎn)換:
    • 如果 S 不是 SX,則執(zhí)行從 S 到 SX 的標(biāo)準(zhǔn)隱式轉(zhuǎn)換。
    • 調(diào)用最具體的從 SX 到 TX 的轉(zhuǎn)換操作符。
    • 如果 TX 不是 T,則執(zhí)行從 TX 到 T 的標(biāo)準(zhǔn)隱式轉(zhuǎn)換。

用戶定義顯式轉(zhuǎn)換

從類型 S 到類型 T 的用戶定義顯式轉(zhuǎn)換(user-defined explicit conversion)的過程如下:

  • 確定類型 S0 和 T0。如果 S 或 T 為可空類型,則 S0 與 T0 為其基礎(chǔ)類型;否則 S0 與 T0 分別等于 S 與 T。
  • 查找類型集 D,從該類型集中考慮用戶定義轉(zhuǎn)換操作符。此集由 S0(若 S0 為類或結(jié)構(gòu))、S0 的基類(若 S0 為類)、 T0(若 T0 為類或結(jié)構(gòu))以及 T0 的基類(若 T0 為類)組成。
  • 查找適用的用戶定義轉(zhuǎn)換操作符與提升操作符集 U,U 由用戶定義隱式與顯示轉(zhuǎn)換符與提升隱式與顯式轉(zhuǎn)換操作符所組成,這些操作符是在 D 中的類或結(jié)構(gòu)內(nèi)聲明的,用于從包含 S 或被 S 包含的類型轉(zhuǎn)換為包含 T 或被 T 包含的類型。如果 U 為空(empty),那么轉(zhuǎn)換未定義(undefined)并出現(xiàn)「編譯時(shí)」錯(cuò)誤。
  • 從 U 中查找最精準(zhǔn)的源類型 SX
    • 如果 U 內(nèi)任何運(yùn)算符均從 S 轉(zhuǎn)換,則 SX 為 S。
    • 否則,如果 U 中的任何運(yùn)算符都從包含 S 的類型轉(zhuǎn)換,則 SX 是這些運(yùn)算符的合并源類型集中被包含程度最大的類型。如果無法找到確切的那個(gè)最大包含類型,那么轉(zhuǎn)換將是不明確的,并伴有「編譯時(shí)」錯(cuò)誤發(fā)生。
    • 否則,SX 在 U 中運(yùn)算符的合并源類型集中時(shí)包含程度最大的類型。如果無法找到確切的那個(gè)最大包含類型,那么轉(zhuǎn)換將是不明確的,并伴有「編譯時(shí)」錯(cuò)誤發(fā)生。
  • 從 U 中查找運(yùn)算符的最精準(zhǔn)的目標(biāo)類型 TX
    • 如果 U 內(nèi)的任意轉(zhuǎn)換符均能轉(zhuǎn)換為 T,那么 TX 為 T。
    • 不然,如果 U 中任何運(yùn)算符都能轉(zhuǎn)換為被 T 包含的類型,則 TX 是這些運(yùn)算符的合并目標(biāo)類型集中最大包含類型。如果無法發(fā)現(xiàn)最大包含程度的類型,則轉(zhuǎn)換是不明確的,并伴有「編譯時(shí)」錯(cuò)誤發(fā)生。
    • 不然, TX 為 U 中運(yùn)算符的合并目標(biāo)類型集中最大包含程度類型。如果無法發(fā)現(xiàn)最大包含程度的類型,則轉(zhuǎn)換是不明確的,并伴有「編譯時(shí)」錯(cuò)誤發(fā)生。
  • 查找最具體的轉(zhuǎn)換運(yùn)算符:
    • 如果 U 只包含一個(gè)從 SX 到 TX 的用戶定義轉(zhuǎn)換操作符,那么這就是最精準(zhǔn)的轉(zhuǎn)換操作符。
    • 不然,如果 U 恰好包含一個(gè)從 SX 到 TX 的提升轉(zhuǎn)換操作符,則這就是最具體的轉(zhuǎn)換操作符。
    • 否則,轉(zhuǎn)換是不明確的,并伴有「編譯時(shí)」錯(cuò)誤發(fā)生。
  • 最后,應(yīng)用轉(zhuǎn)換:
    • 如果 S 不是 SX,則執(zhí)行從 S 到 SX 的標(biāo)準(zhǔn)顯示轉(zhuǎn)換。
    • 調(diào)用最具體的從 SX 到 TX 的用戶定義轉(zhuǎn)換操作符。
    • 如果 TX 不是 T,則執(zhí)行從 TX 到 T 的標(biāo)準(zhǔn)顯式轉(zhuǎn)換。

匿名函數(shù)轉(zhuǎn)換

anonymous-method-expressionlambda-expression 都被歸類到了匿名函數(shù)(anonymous function,第七章第十五節(jié))。此表達(dá)式不具有類型,但可隱式轉(zhuǎn)換為委托類型或表達(dá)式樹類型。具體而言,匿名函數(shù) F 可以與委托類型 D 兼容(compatible with):

  • 如果 F 包含 anonymous-function-signature,則 D 與 F 具有相同數(shù)量的形參個(gè)數(shù)。
  • 如果 F 不包含 anonymous-function-signature,那則 D 可以具有零或多個(gè)任意類型的形參,但 D 的任何形參都沒有 out 參數(shù)修飾符。
  • 如果 F 具有顯式類型化的形參列表(explicitly typed parameter list),則 D 中每一個(gè)形參都具有與 F 中對(duì)應(yīng)形參相同的類型與修飾符。
  • 如果 F 具有隱式類型化的形參列表(implicitly typed parameter list),則 D 沒有 refout 形參。
  • 如果 F 的主體是表達(dá)式且 D 具有空(void)返回類型,或者 F 是異步(async)的且 D 的返回類型為 Task,則當(dāng) F 中每一個(gè)形參都被給定為 D 中對(duì)應(yīng)參數(shù)的類型時(shí),F(xiàn) 的主體是有效表達(dá)式(valid expression,請(qǐng)參考第七章),該表達(dá)式將允許作為 statement-expression(第八章第六節(jié))。
  • 如果 F 的主體是語句塊且 D 具有空(void)返回類型,或者 F 是異步(async)的且 D 的返回類型為 Task,則將 F 中每一個(gè)形參都被給定為 D 中對(duì)應(yīng)參數(shù)的類型時(shí),F(xiàn) 的主體是有效語句塊(valid statement block,請(qǐng)參考第八章第二節(jié)),該語句塊沒有 return 語句指定了表達(dá)式。
  • 如果 F 的主體是表達(dá)式,并且 F 為非異步且 D 具有非空返回類型 T,或者是 F 為異步且 D 具有返回類型為 Task<T>,則當(dāng) F 的每一個(gè)形參都被給定為 D 中對(duì)應(yīng)參數(shù)的類型時(shí),F(xiàn) 的主體是有效表達(dá)式(valid expression,請(qǐng)參考第七章),該表達(dá)式可隱式轉(zhuǎn)換為 T。
  • 如果 F 的主體是語句塊,并且 F 為非異步且 D 具有非空返回類型 T,或者是 F 為異步且 D 具有返回類型為 Task<T>,則當(dāng) F 的每一個(gè)形參都被給定為 D 中對(duì)應(yīng)參數(shù)的類型時(shí),F(xiàn) 的主體是有效語句塊(valid statement block,請(qǐng)參考第八章第二節(jié)),該表達(dá)式可隱式轉(zhuǎn)換為 T。

下文使用任務(wù)類型的簡(jiǎn)寫 TaskTask<T>(第十章第十四節(jié))。

如果 F 能與委托類型 D 兼容,則 Lambda 表達(dá)式 F 能與表達(dá)式樹類型 Expression<D> 兼容。注意,此處不適用于匿名方法,而僅適用于 Lambda 表達(dá)式。

某些 Lambda 表達(dá)式不能被轉(zhuǎn)換為表達(dá)式樹類型——即使存在轉(zhuǎn)換,該過程也會(huì)在「編譯時(shí)」失敗。這種情況會(huì)發(fā)生在符合以下條件的時(shí)候:

  • 具有 block 體;
  • 包含簡(jiǎn)單的或復(fù)合的賦值操作符;
  • 包含動(dòng)態(tài)綁定表達(dá)式;
  • 是異步的。

在下面例子中使用了泛型委托類型 Func<A, R>,該函數(shù)采用一個(gè)類型為 A 的實(shí)參并返回類型為 R 的值:

delegate R Func<A,R>(A arg);

在下面賦值中,

Func<int,int> f1 = x => x + 1;              // OkFunc<int,double> f2 = x => x + 1;           // OkFunc<double,int> f3 = x => x + 1;           // 錯(cuò)誤Func<int, Task<int>> f4 = async x => x + 1; // Ok

每一個(gè)匿名函數(shù)的形參和返回值類型都由匿名函數(shù)所賦予的變量的類型來確定。

第一個(gè)賦值成功地把匿名函數(shù)轉(zhuǎn)換為委托類型 Func<int, int>,因?yàn)楫?dāng) x 指定的類型是 int 的時(shí)候,x + 1 是一個(gè)可以隱式轉(zhuǎn)換為 int 類型的有效表達(dá)式。

同樣地,第二個(gè)賦值成功地把匿名函數(shù)轉(zhuǎn)換為委托類型 Func<int, double>,這是因?yàn)?x + 1 的結(jié)果(類型為 int)可以隱式轉(zhuǎn)換為類型 double。

然而,第三個(gè)賦值會(huì)出現(xiàn)「編譯時(shí)錯(cuò)誤」,這是因?yàn)?x 給定的是 double 類型,x + 1 的結(jié)果(類型為 double)不能隱式轉(zhuǎn)換為 int。

第四個(gè)賦值成功地把匿名異步函數(shù)轉(zhuǎn)換為委托類型 Func<int, Task<int>>,這是因?yàn)?x + 1 的結(jié)果(類型為 int)可以隱式轉(zhuǎn)換為任務(wù)類型 Task<int> 的結(jié)果類型 int。

匿名函數(shù)可能會(huì)影響重載策略(overload resolution),并參與類型推斷(type inference),關(guān)于這一點(diǎn)請(qǐng)參見第七章第五節(jié)。

匿名函數(shù)轉(zhuǎn)換為委托類型的計(jì)算

匿名函數(shù)到委托類型的轉(zhuǎn)換將產(chǎn)生委托實(shí)例,這個(gè)委托實(shí)例引用匿名函數(shù)以及被捕獲的處于活動(dòng)狀態(tài)的外部變量的集合(可以為空)。當(dāng)委托被調(diào)用,將執(zhí)行匿名函數(shù)體。使用委托所引用的被捕獲的外部變量集執(zhí)行方法主體中的代碼。

自匿名函數(shù)所產(chǎn)生的委托的調(diào)用列表只含一項(xiàng),確切目標(biāo)對(duì)象與委托的目標(biāo)方法并未被明確指定。具體來說,沒有具體指定該委托的目標(biāo)對(duì)象是空(null)、所閉包的函數(shù)成員的 this 值,還是其它某個(gè)對(duì)象。

將具有相同的被捕獲外層變量實(shí)例集(可能為空集)的語義上相同的匿名函數(shù)轉(zhuǎn)換到委托類型,允許(但不要求)返回相同的委托實(shí)例。術(shù)語「語義上相同」表示在任何情況下,只要給定相同的參數(shù),匿名函數(shù)的執(zhí)行就會(huì)產(chǎn)生相同的結(jié)果。這條規(guī)定允許優(yōu)化如下面這樣的代碼:

delegate double Function(double x);class Test{    static double[] Apply(double[] a, Function f) {        double[] result = new double[a.Length];        for (int i = 0; i < a.Length; i++) result[i] = f(a[i]);        return result;    }    static void F(double[] a, double[] b) {        a = Apply(a, (double x) => Math.Sin(x));        b = Apply(b, (double y) => Math.Sin(y));        ...    }}

由于兩個(gè)匿名函數(shù)委托存在相同的(空集)被捕獲外層變量集,且這兩個(gè)匿名函數(shù)語義上相同,所以編譯器被允許使用這兩個(gè)委托引用同一個(gè)目標(biāo)方法。實(shí)際上,編譯器甚至被允許從這兩個(gè)匿名函數(shù)表達(dá)式返回同一個(gè)委托實(shí)例(delegate instance)。

匿名函數(shù)轉(zhuǎn)換為表達(dá)式樹類型的計(jì)算

將匿名函數(shù)轉(zhuǎn)換為表達(dá)式樹類型會(huì)產(chǎn)生一個(gè)表達(dá)式樹(expression tree,第四章第六節(jié))。準(zhǔn)確地講,匿名函數(shù)轉(zhuǎn)換計(jì)算會(huì)導(dǎo)致構(gòu)造對(duì)象結(jié)構(gòu)——表示匿名函數(shù)本身的結(jié)構(gòu)。表達(dá)式樹的精確結(jié)構(gòu)以及創(chuàng)建該目錄樹的確切過程為定義的實(shí)現(xiàn)(implementation defined)。

實(shí)現(xiàn)舉例

本節(jié)從其它 C# 構(gòu)造的角度描述可能的匿名函數(shù)轉(zhuǎn)換實(shí)現(xiàn)方法。此處所描述的實(shí)現(xiàn)基于 Microsoft C# 編譯器 所使用的相同原理(same principles),但這不意味著強(qiáng)制實(shí)現(xiàn),也不是唯一實(shí)現(xiàn)方式。此處僅簡(jiǎn)單介紹到表達(dá)式樹的轉(zhuǎn)換,因?yàn)樗鼈兊臉?biāo)準(zhǔn)語義超出了本規(guī)范的大綱范圍。

本節(jié)其余部分舉了幾個(gè)不同特點(diǎn)匿名函數(shù)的例子。在每個(gè)例子中,提供了到僅使用其他 C# 構(gòu)造的代碼的相應(yīng)轉(zhuǎn)換。在這些例子中,設(shè)標(biāo)識(shí)符 D 表示下面委托類型:

public delegate void D();

匿名函數(shù)的最簡(jiǎn)形式是不捕獲外層變量(outer variables)的形式:

class Test{    static void F() {        D d = () => { Console.WriteLine("test"); };    }}

這可以轉(zhuǎn)換為引用編譯器生成的靜態(tài)方法,而匿名方法就在該靜態(tài)方法內(nèi):

class Test{    static void F() {        D d = new D(__Method1);    }    static void __Method1() {        Console.WriteLine("test");    }}

在下例中,匿名函數(shù)引用 this 實(shí)例成員:

class Test{    int x;    void F() {        D d = () => { Console.WriteLine(x); };    }}

這可以轉(zhuǎn)換為編譯器生成的實(shí)例方法(包含該匿名方法的代碼):

class Test{    int x;    void F() {        D d = new D(__Method1);    }    void __Method1() {        Console.WriteLine(x);    }}

在這個(gè)例子中,匿名函數(shù)捕獲一個(gè)本地局部變量:

class Test{    void F() {    int y = 123;        D d = () => { Console.WriteLine(y); };    }}

局部變量的生命周期現(xiàn)在至少被延長(zhǎng)到匿名函數(shù)委托的生命周期。這可以通過將局部變量「提升」到編譯器生成的類的字段來實(shí)現(xiàn)。局部變量的實(shí)例化(第七章第 15.2 節(jié))將對(duì)應(yīng)為編譯器生成的類創(chuàng)建實(shí)例,且訪問局部變量則對(duì)應(yīng)訪問編譯器生成的類的實(shí)例中的字段。而且匿名函數(shù)成為編譯器生成的類的實(shí)例方法:

class Test{    void F() {        __Locals1 __locals1 = new __Locals1();        __locals1.y = 123;        D d = new D(__locals1.__Method1);    }    class __Locals1    {        public int y;        public void __Method1() {            Console.WriteLine(y);        }    }}

最后,下面這個(gè)匿名函數(shù)捕獲 this 以及兩個(gè)具有不同生命周期的局部變量:

class Test{    int x;    void F() {        int y = 123;        for (int i = 0; i < 10; i++) {            int z = i * 2;            D d = () => { Console.WriteLine(x + y + z); };        }    }}

這里,編譯器將對(duì)所捕獲的局部變量的每一個(gè)語句塊創(chuàng)建了一個(gè)類,這樣不同語句塊中的局部變量將具有獨(dú)立的生命周期。__Locals2 的實(shí)例——編譯器為內(nèi)部語句塊創(chuàng)建的類——包含局部變量 z 以及引用 __Locals1 的實(shí)例字段。__Locals1 的實(shí)例——編譯器為外部語句塊創(chuàng)建的類——包含局部變量 y 以及引用包容函數(shù)成員 this 的字段。對(duì)于這些數(shù)據(jù)結(jié)構(gòu),可以通過 __Locals2 的實(shí)例來獲得所有被捕獲的外層變量,匿名函數(shù)的代碼從而可以實(shí)現(xiàn)為該類的實(shí)例方法。

class Test{    void F() {        __Locals1 __locals1 = new __Locals1();        __locals1.__this = this;        __locals1.y = 123;        for (int i = 0; i < 10; i++) {            __Locals2 __locals2 = new __Locals2();            __locals2.__locals1 = __locals1;            __locals2.z = i * 2;            D d = new D(__locals2.__Method1);        }    }    class __Locals1    {        public Test __this;        public int y;    }    class __Locals2    {        public __Locals1 __locals1;        public int z;        public void __Method1() {            Console.WriteLine(__locals1.__this.x + __locals1.y + z);        }    }}

此處用于捕獲局部變量的技術(shù)也可以用于將匿名函數(shù)轉(zhuǎn)換為表達(dá)式樹:對(duì)變意義所創(chuàng)建的對(duì)象的引用能存儲(chǔ)在表達(dá)式樹中,并對(duì)局部變量的訪問可以表示為對(duì)這些對(duì)象的字段的訪問。這種方法(approach)的優(yōu)勢(shì)在于允許「提升的(lifted)」局部變量在委托和表達(dá)式樹之間共享。


方法組轉(zhuǎn)換

存在從方法組(method group,第七章第一節(jié))到兼容委托類型的隱式轉(zhuǎn)換(第六章第一節(jié))。對(duì)于給定的委托類型 D 以及歸類為方法組的表達(dá)式 E,如果 E 包含至少一個(gè)能以其正常形式(第七章第 5.3.1 節(jié))應(yīng)用于使用 D 的形參類型與修飾符構(gòu)造的實(shí)參列表的方法,則存在從 E 到 D 的隱式轉(zhuǎn)換。具體為:

從方法組 E 到委托類型 D 的轉(zhuǎn)換時(shí)「編譯時(shí)」應(yīng)用在下面的部分中描述。注意,存在從 E 到 D 的隱式轉(zhuǎn)換并不保證轉(zhuǎn)換產(chǎn)生的「編譯時(shí)」應(yīng)用會(huì)不帶錯(cuò)誤地成功。

  • 對(duì)于 E(A) 形式的方法調(diào)用(method invocation,第七章第 6.5.1 節(jié)),僅選擇單個(gè)方法 M,并進(jìn)行下列變更(modifications):
    • 實(shí)參清單 A 是一個(gè)表達(dá)式列表,每個(gè)都可分類為變量且具有與 D 的 formal-parameter-list 的形參對(duì)應(yīng)的類型與修飾符(ref 或 out)。
    • 所考慮的候選方法(candidate methods)僅為那些可以正常形式(第七章第 5.3.1 節(jié))加以應(yīng)用的方法,而不是那些只可以其開展形式引用之方法。
  • 如果第七章第 6.5.1 節(jié)的算法產(chǎn)生錯(cuò)誤,那么「編譯時(shí)」將發(fā)生錯(cuò)誤。否則,該算法將生成一個(gè)產(chǎn)生參數(shù)個(gè)數(shù)與 D 相同的最佳方法 M,并認(rèn)為存在此種轉(zhuǎn)換。
  • 所選的方法 M 必須兼容(第十五章第二節(jié))于委托類型 D,否則將會(huì)出現(xiàn)「編譯時(shí)」錯(cuò)誤。
  • 如果所選的方法 M 為實(shí)例方法,則與 E 相關(guān)的實(shí)例表達(dá)式確定委托的目標(biāo)類型。
  • 如果所選擇的方法 M 為通過實(shí)例表達(dá)式訪問表示的擴(kuò)展方法,,則該實(shí)例表達(dá)式將確定委托的目標(biāo)對(duì)象。
  • 轉(zhuǎn)換結(jié)果是類型為 D 的值,一個(gè)引用選定方法與目標(biāo)方法的新創(chuàng)建的委托。

注意,以下情況中,此過程可能會(huì)導(dǎo)致所創(chuàng)建到擴(kuò)展方法(extension method)的委托:第七章第 6.5.1 節(jié)的算法未能找到實(shí)例方法,但在以擴(kuò)展方法調(diào)用(第七章第 6.5.2 節(jié))的形式處理 E(A) 的調(diào)用時(shí)卻取得成功。故而創(chuàng)建委托將捕獲該方法的第一個(gè)實(shí)參。

下面例子展示了方法組的轉(zhuǎn)換:

delegate string D1(object o);delegate object D2(string s);delegate object D3();delegate string D4(object o, params object[] a);delegate string D5(int i);class Test{    static string F(object o) {...}    static void G() {        D1 d1 = F;    // Ok        D2 d2 = F;    // Ok        D3 d3 = F;    // Error – not applicable        D4 d4 = F;    // Error – not applicable in normal form        D5 d5 = F;     // Error – applicable but not compatible    }}

對(duì)于 d1 的賦值隱式地將方法組 F 轉(zhuǎn)換為 D1 類型的值。

對(duì)于 d2 的賦值展示如何創(chuàng)建到具有派生程度較小(逆變)的形參類型和派生程度較大(協(xié)變)的返回類型的方法的委托。

對(duì)于 d3 的賦值展示當(dāng)方法不適用時(shí)如何不不存在轉(zhuǎn)換。

對(duì)于 d4 的賦值展示方法如何必須以其正常形式應(yīng)用。

對(duì)于 d5 的賦值展示如何允許委托和方法的形參與返回值類型僅對(duì)引用類型不同。

與其它隱式和顯式轉(zhuǎn)換一樣,強(qiáng)制轉(zhuǎn)換操作符可以用于顯式執(zhí)行方法組轉(zhuǎn)換。因此下面這個(gè)例子

object obj = new EventHandler(myDialog.OkClick);

可以寫成

object obj = (EventHandler)myDialog.OkClick;

方法組可能影響重載策略,并參與類型推斷,具體參見第七章第五節(jié)。

方法組轉(zhuǎn)換的「運(yùn)行時(shí)」運(yùn)算如下所述:

  • 如果「編譯時(shí)」所選擇的方法是實(shí)例方法,或是一個(gè)對(duì)實(shí)例方法訪問的擴(kuò)展方法,則委托的目標(biāo)對(duì)象由與 E 相關(guān)的實(shí)例表達(dá)式來確定:
    • 計(jì)算實(shí)例表達(dá)式(instance expression)。如果計(jì)算發(fā)生異常,則不執(zhí)行接下來的步驟。
    • 如果實(shí)例表達(dá)式為 reference-type,則由實(shí)例表達(dá)式運(yùn)算所得的值將為目標(biāo)對(duì)象。如果所選的方法是實(shí)例方法且目標(biāo)對(duì)象為空(null),則拋出 System.NullReferenceException 異常并不再執(zhí)行后續(xù)步驟。
    • 如果實(shí)例表達(dá)式是 value-type,則執(zhí)行裝箱操作(boxing operation,第四章第 3.1 節(jié))以將值變?yōu)閷?duì)象,然后成為目標(biāo)對(duì)象。
  • 不然,所選的方法為靜態(tài)方法(static method)調(diào)用的一部分,而委托的目標(biāo)對(duì)象是空(null)。
  • 委托類型 D 的新實(shí)例被分配了存儲(chǔ)位置。如果沒有足夠的內(nèi)存空間可被分配給新實(shí)例,則會(huì)拋出 System.OutOfMemoryException 異常,并不執(zhí)行接下來的步驟。
  • 用隊(duì)在「編譯時(shí)」確定的方法的引用和對(duì)上面運(yùn)算所得的目標(biāo)對(duì)象的引用初始化新委托實(shí)例。

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 湘西| 安庆市| 乌苏市| 布尔津县| 乌什县| 蓬莱市| 昌黎县| 晋宁县| 武安市| 南乐县| 兴化市| 衢州市| 黎平县| 汉源县| 五常市| 日土县| 绵竹市| 留坝县| 肥城市| 兴和县| 嵊州市| 五大连池市| 嘉黎县| 错那县| 富蕴县| 山西省| 天等县| 宁城县| 晴隆县| 汉阴县| 雷波县| 武陟县| 乐安县| 榆树市| 辽阳市| 杭锦后旗| 台湾省| 南安市| 白山市| 黔江区| 清河县|