亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

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

2019-11-14 13:59:50
字體:
來源:轉載
供稿:網友

C# Language Specification 5.0

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

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

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


隱式轉換

以下轉換被分類為隱式轉換:

  • 標識(identity)轉換
  • 隱式數值轉換
  • 隱式枚舉轉換
  • 隱式可空值轉換
  • 空值文本轉換
  • 隱式引用轉換
  • 裝箱轉換
  • 隱式動態(dynamic)轉換
  • 隱式常量表達式轉換
  • 用戶定義匿名轉換
  • 匿名函數轉換
  • 方法組轉換

隱式換磚(implicit conversions)可發生于多種情況下,包括函數成員調用(第七章第 5.4 節)、強值轉換表達式(cast exPRessions,第七章第 7.6 節)以及賦值(第七章第十七節)。

預定義隱式轉換(pre-defined implicit conversions)總是成功的,永不會導致異常拋出。正確設計用戶定義隱式轉換(user-defined implicit conversions)也能表現(exhibit)出這些特征(characteristics)。

對于轉換的目的來說,objectdynamic 類型被視為等價。

然而,動態轉換(dynamic conversions,第六章第 1.8 節以及第六章第 2.6 節)僅適用于類型為 dynamic(第四章第七節)的表達式。

標識轉換

標識轉換(identity conversion)可將任意類型轉換為其相同類型。這種轉換之所以存在,是因為要讓已是所需類型的實體能被認為是可轉換(為該類型)的。

由于 objectdynamic 被視為等價,所以在 objectdynamic 之間以及在即將出現的所有 dynamic 轉為 object 的轉換具有相同構造類型的之間,存在一個標識轉換。

隱式數值轉換

隱式數值轉換(implicit numeric conversions)包括:

  • sbyteshort、intlong、float、doubledecimal;
  • byteshortushort、int、uint、long、ulong、float、doubledecimal;
  • shortintlong、floatdoubledecimal;
  • ushortintuint、longulong、float、doubledecimal
  • intlong、float、doubledecimal
  • uintlong、ulongfloat、doubledecimal
  • longfloat、doubledecimal;
  • ulongfloat、doubledecimal
  • charushort、int、uint、long、ulong、float、doubledecimal;
  • floatdouble。

從 int、uint、long 或 ulong 轉換為 float,從 long 或 ulong 轉換為 double 會丟失精度(loss of precision),但不會導致數量級的丟失(loss of magnitude)。其它的隱式數制轉換不會丟失任何信息。

不存在任何向 char 類型的隱式轉換,所以任何數值類型的值都不會自動轉換為字符類型。

隱式枚舉轉換

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

隱式可空值轉換

對不可為空值類型(non-nullable value types)的預定義隱式轉換操作也可以用于其可空類型的版本上。對于每個從非可空值類型 S 到非可空值類型 T 的預定義隱式標識和數值轉換,都存在如下可空的隱式轉換:

  • S?T? 的隱式轉換;
  • ST? 的隱式轉換。

基于從 S 到 T 的基礎轉換來計算隱式可空轉換的過程如下:

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

空值文本轉換

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

隱式引用轉換

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

  • 從任何 reference-typeobjectdynamic;
  • 從任何 class-type S 到任何 class-type T(前提是 S 是從 T 派生的);
  • 從任何 class-type S 到任何 interface-type T(前提是 S 是 T 的實現)
  • 從任何 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 的隱式引用轉換。
  • 從任何 array-typeSystem.Array 及其實現的接口;
  • 從單維度數組類型 S[]System.Collections.Generic.IList<T> 及其基接口(前提是存在從 S 到 T 的隱式標識或引用轉換);
  • 從任何 delegate-typeSystem.Delegate 及其實現的接口;
  • 從空值文本(null literal)到任何 reference-type;
  • 從任何 reference-typereference-type T(前提是其具有到 reference-type T0 的隱式標識或引用轉換,且 T0 具有到 T 的標識轉換);
  • 從任何 reference-type 到接口或委托類型 T(前提是具有到接口或委托類型 T0 的隱式標識或引用轉換,且 T0 為可變化轉換(variance-convertible,第十三章第 1.3.2 節)到 T);
  • 涉及到已知引用類型的類型形參的隱式轉換。有關涉及類型形參的隱式轉換的信息參見第六章第 1.10 節。

隱式引用轉換是在 reference-type 之間的轉換,可以證明這種轉換總能成功,故而不需要在「運行時」對其進行檢查。

引用轉換,不管是顯式還是隱式,都不會改變被轉換的對象的引用標識(referential identity)。換而言之,雖然引用轉換可以改變引用的類型,但不會改變所引用的對象的類型或值

裝箱轉換

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

存在從可空類型到引用類型的裝箱轉換,當且僅當該可空類型的不可空值類型存在向引用類型裝箱轉換。

如果值類型具有到接口類型 I0 的裝箱轉換,且 I0 具有到接口類型 I 的標識轉換,則值類型具有慈寧到 I 的裝箱轉換。

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

對非可空值類型的值的裝箱可以包括以下操作:分配一個對象實例,然后將值類型的值復制進該實例。結構可裝箱為類型 System.ValueType,因為它是所有結構的基類型(第十一章第 3.2 節)。

對非可空類型的值的裝箱按以下步驟進行:

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

關于裝箱轉換的介紹請閱讀第四章第 3.1 節。

隱式動態轉換

存在從 dynamic 類型表達式到任何類型 T 的隱式動態轉換(implicit dynamic conversion)。轉換是動態綁定的(dynamically bound,第七章第 2.2 節),這意味著在「運行時」能發現從表達式的「運行時」類型到類型 T 的隱式轉換。如果未發現任何轉換,則拋出「運行時」異常。

注意這種隱式轉換似乎違背了第六章第一節開頭部分的建議,隱式轉換不應該導致異常。但這不是轉換自身導致的異常,而是轉換時動詞「發現」(finding)所導致的異常?!高\行時」異常的風險是使用動態綁定所固有的。如果不需要動態綁定轉化,表達式首先轉換為一個 object,然后轉換為所需的類型。

下例說明了隱式動態轉換:

object o  = “object”dynamic d = “dynamic”;string s1 = o; // 「編譯時」失敗,不存在轉換string s2 = d; // 編譯且「運行時」成功int i     = d; // 編譯但「運行時」失敗,不存在轉換

s2i 的賦值都使用了隱式動態轉換(implicit dynamic conversions),所綁定的操作會一直掛起,直到「運行時(run-time)」才執行。在「運行時」,可以看到從 d 的「運行時」類型(string)到目標類型的隱式轉換。轉換會找到 string 而不是 int。

隱式常量表達式轉換

隱式常量表達式轉換(implicit constant expression conversions)允許以下轉換:

  • int 類型的 constant-expression(第七章第十九節)能被轉換為 sbyte, byte, short, ushort, uintulong,所提供的轉換結果在目標類型的合法區間內。
  • long 類型的 constant-expression 能被轉換為 ulong,所提供的結果為非負數。

涉及類型形參的隱式轉換

對于給定類型形參 T 存在下列隱式轉換:

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

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

用戶定義隱式轉換

用戶定義隱式轉換(user-defined implicit conversion)由可選的標準隱式轉換(optional standard implicit conversion)、執行用戶定義隱式轉換的操作符以及另一個可選的基礎隱式轉換等三部分組成。運行用戶定義隱式轉換的確切規則詳見第六章第 4.4 節。

匿名函數轉換與方法組轉換

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


顯式轉換

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

  • 所有的隱式轉換
  • 顯式數值轉換
  • 顯式枚舉轉換
  • 顯式可空值轉換
  • 顯式引用轉換
  • 顯示接口轉換
  • 拆箱轉換
  • 顯式動態轉換
  • 用戶定義顯式轉換

顯式轉換可出現在強制轉換表達式(cast expressions,第七章第 7.6 節)內。

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

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

顯式數值轉換

顯示數值轉換(explicit numeric conversions)是指從 numeric-type 到另一個 numeric-type 的轉換,這種轉換不能用已知的隱式數值轉換(第六章第 1.2 節)來實現,它包括:

  • 從 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

由于顯式轉換包括所有隱式轉換和顯式數值轉換,所以其總可以使用強制轉換表達式(cast expression,第七章第 7.6 節)將任意 numeric-type 轉換為其它任意 numeric-type。

顯式數值轉換可能會丟失信息或導致異常拋出。顯式數值轉換的處理過程如下:

  • 對于從一個整型轉換為另一個整型,其處理取決于轉換發生時的溢出檢查上下文(overflow checking context,第七章第 6.12 節):
    • 在 checked 上下文中,如果源操作數的值在目標類型的區間內,轉換成功;但如果超出了目標類型的合法區間,則會拋出 System.OverflowException 異常。
    • 在 unchecked 上下文中,轉換總會成功,其操作過程為:
      • 如果源類型大于目標類型,則源值通過丟棄精度使其截斷。然后將其結果視作目標類型的值。
      • 如果源類型小于目標類型,則源值或按符號擴展(sign-extended)或按零值擴展(zero-extended),使其大小與目標值相同。符號擴展用于有符號的源類型,零值擴展用于無符號的源類型。然后將其結果視作目標類型的值。
      • 如果源類型的大小與目標類型相同,那么源值將視為目標類型的值。
  • 對于從 decimal 到整型的轉換,源值向零舍入(rounded towards zero)到最接近的整數值,而這個值即為轉換的結果。如果所得的整數值超出目標類型的范圍,則會拋出 System.OverflowException 異常。
  • 對于從 float 或 double 到整型的轉換,其處理過程取決于執行轉換過程中的溢出檢查上下文(overflow checking context,第七章第 6.12 節):
    • 在 checked 上下文中,轉換處理的過程如下:
      • 如果操作數的值是 NaN 或無限大(infinite),那么會拋出 System.OverflowException 異常。
      • 否則,源操作數會向零舍入到最接近的整數值。如果整數值在目標類型的范圍內,則將其作為轉換之結果。
      • 不然,則拋出 System.OverflowException 異常。
    • 在 unchecked 上下文,無論如何轉換均會成功,其處理過程如下:
      • 如果操作數是 NaN 或無限大(infinite),則轉換結果為目標類型的一個未經指定的值(unspecified value)。
      • 否則,源操作數向零舍入到最接近的整型數。如果整數值位于目標類型的范圍內,則將其作為轉換之結果予以返回。
      • 不然,返回一個目標類型的未經指定的值。
  • 對于從 double 到 float 的轉換,double 值將舍入到最接近的 float 值。如果 double 值太小,無法表示為 float,則結果為正零(positive zero)或負零(negative zero)。如果 double 值過大,無法表示為 float 值,則其結果為正無窮大(positive infinity)或負無窮大(negative infinity)。如果 double 是 NaN,則結果也返回 NaN。
  • 對于從 float 或 double 到 decimal 的轉換,源值將轉換為 decimal 表示形式,并在需要時將其第二十八位小數向上舍入(第四章第 1.7 節)到最接近的數字。如果源值太小,無法表示為 decimal,則結果變為零。如果源值是 NaN、無限大或太大而無法表示為 decimal,則拋出 System.OverflowException 異常。
  • 對于從 decimal 到 float 或 double 的轉換,decimal 的值將舍入為最接近 double 或 float 的值。雖然這種轉換會丟失精度,但從不會拋出異常。

顯式枚舉轉換

顯式枚舉轉換(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。

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

顯式可空值轉換

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

  • S?T? 的顯式轉換。
  • ST? 的顯式轉換。
  • S?T 的顯式轉換。

基于從 ST 的基礎轉換的可空轉換運算步驟如下:

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

注意,如果對一個為 null 值的可空值進行解包會引發異常。

顯式引用轉換

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

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

顯式引用轉換是需要在「運行時」檢查其正確的 reference-type 之間進行的轉換。

為了顯式引用轉換在「運行時」成功,源操作數的值必須為空(null),或源操作數所引用對象的實際類型必須是一個能通過隱式引用轉換(第六章第 1.6 節)或裝箱轉換(第六章第 1.7 節)轉換為目標類型的類型。如果顯式引用轉換失敗,會拋出 System.InvalidCastException 異常。

引用轉換,無論是顯示還是隱式,都不會改變被轉換對象的引用標識(referential identity)。換句話說,雖然引用轉換可以改變所引用的類型,但從不會改變所引用對象的類型或值。

拆箱轉換

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

存在從引用類型到 nullable-type 的拆箱轉換,前提是存在從該引用類型到 nullable-type 的基礎類型 non-nullable-value-type 的拆箱轉換。

如果值類型 S 具有從接口類型 I 的拆箱轉換,且 I0 具有從接口類型到 I 的標識轉換,則其具有來自 I 的拆箱轉換。

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

拆箱操作包括以下步驟:一是檢查對象實例是否為給定值類型的裝箱值,二是將該值復制出該實例。對一個 nullable-type 類型的 null 引用將產生該類型的 null 值。結構可以從類型 System.ValueType 拆箱,因為該類型是所有結構的基類(第十一章第 3.2 節)。

更多拆箱轉換的介紹可查看第四章第 3.2 節。

顯式動態轉換

存在從 dynamic 到任意類型 T 的顯式動態轉換(explicit dynamic conversion)。轉換時動態綁定(第七章第 2.2 節)的,這意味著在「運行時」時,可以看到從表達式的「運行時」類型到 T 的顯式轉換。如果沒有轉換發生,那么將拋出「運行時」異常。

如果轉換不需要動態綁定,表達式可以先轉換為 object,然后轉為所需類型。

如下例所定義:

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

下例列舉了顯式動態轉換:

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

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

涉及類型形參的顯式轉換

對于給定的類型形參 T 存在下列顯式轉換:

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

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

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

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

如果允許從 t 直接轉換為 int,極有可能會認為 x<int>.F(7) 將返回 7L。然而并不是如此,因為僅當綁定時(binding-time)已知類型為數字類型時,才會考慮標準的數字轉換(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    }}

這段代碼現在能夠編譯,但當「運行時」執行 X<int>.F(7) 時會拋出異常,這是因為不能將已裝箱的 int 直接轉換為 long。

用戶定義顯式轉換

用戶定義顯式轉換(user-defined explicit conversion)包括以下三部分:可選的標準顯式轉換、執行用戶定義的隱式或顯式轉換操作、另一個可選的基礎顯式轉換。關于計算用戶定義顯式轉換的確切規則詳見第六章第 4.5 節。


標準轉換

標準轉換(standard conversions)是作為用戶定義轉換(user-defined conversion)的一部分出現的預定義轉換(pre-defined conversions)

標準隱式轉換

下列隱式轉換被劃入標準隱式轉換(standard implicit conversions):

  • 標識轉換(第六章第 1.1 節)
  • 隱式數值轉換(第六章第 1.2 節)
  • 隱式可空轉換(第六章第 1.4 節)
  • 隱式引用轉換(第六章第 1.6 節)
  • 裝箱轉換(第六章第 1.7 節)
  • 隱式常量表達式轉換(第六章第 1.8 節)
  • 關于類型形參的隱式轉換(第六章第 1.10 節)

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

標準顯式轉換

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


用戶定義轉換

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

許可的用戶定義轉換

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

對于給定源類型 S 和目標類型 T,如果 S 或 T 均為可空類型,設 S0 及 T0 引用其基礎類型,否則 S0 及 T0 分別等于 S 與 T。只有當以下條件為真時,類型或結構允許聲明從源類型 S 到目標類型 T 的轉換:

  • S0 與 T0 是不同類型。
  • S0 或 T0 中有一個是聲明了該運算符的類或結構。
  • S0 與 T0 都不是 interface-type
  • 除用戶定義轉換外,不存在從 S 到 T 或從 T 到 S 的轉換。

適用于用戶定義轉換的限制將在第十章第 10.3 節中進一步討論。

提升轉換操作符

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

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

用戶定義轉換的計算

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

  • 從能被考慮的用戶定義轉換操作符的類或結構集中查找。此集由源類型及其基類與目標類型及其基類構成(隱式假定只有類和結構能被聲明用戶定義操作符,且非類類型不具有基類)。為了執行本步驟,如果源類型或目標類型為 nullable-type,則改為使用其基礎類型。
  • 透過該類型集,確定哪一個用戶定義轉換操作符和提升轉換操作符可被使用。對于可用的轉換操作符而言,其必須可以執行標準轉換(第六章第三節)來使源類型可被轉換為該運算符操作數所要求的類型,且必須可以執行標準轉換來使運算符的結果類型轉換為目標類型。
  • 通過可用的用戶定義操作符集,確定哪一個操作符是最為精準的??偟膩碚f,最精準的操作符是操作數類型「最接近」源類型且結果類型「最接近」目標類型的運算符。用戶定義轉換操作符優先級高于提升轉換操作符。隨后章節定義了最精準用戶定義轉換運算符的確切規則。

當最精準用戶定義轉換操作符被明確了之后,確切的執行步驟分為三步:

  • 首先,如果需要,執行標準轉換,將源類型轉換為用戶定義轉換運算符或提升轉換運算符的操作數所要求的類型;
  • 第二步,調用用戶定義或提升轉換操作符執行轉換;
  • 最后,如果需要,執行基礎轉換,將用戶定義或提升轉換操作符的結果轉換為目標類型。

用戶定義轉換的執行不會調用另一個用戶定義轉換或提升轉換操作符。換句話來說,從類型 S 到類型 T 的轉換將不會首選調用從 S 到 X 的用戶定義轉換,而后再調用從類型 X 到類型 T 的用戶定義轉換。

用戶定義轉換或顯式轉換的確切定義將在后述章節給出。這些定義使用以下術語:

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

用戶定義隱式轉換

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

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

用戶定義顯式轉換

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

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

匿名函數轉換

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

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

下文使用任務類型的簡寫 TaskTask<T>(第十章第十四節)。

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

某些 Lambda 表達式不能被轉換為表達式樹類型——即使存在轉換,該過程也會在「編譯時」失敗。這種情況會發生在符合以下條件的時候:

  • 具有 block 體;
  • 包含簡單的或復合的賦值操作符;
  • 包含動態綁定表達式;
  • 是異步的。

在下面例子中使用了泛型委托類型 Func<A, R>,該函數采用一個類型為 A 的實參并返回類型為 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;           // 錯誤Func<int, Task<int>> f4 = async x => x + 1; // Ok

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

第一個賦值成功地把匿名函數轉換為委托類型 Func<int, int>,因為當 x 指定的類型是 int 的時候,x + 1 是一個可以隱式轉換為 int 類型的有效表達式。

同樣地,第二個賦值成功地把匿名函數轉換為委托類型 Func<int, double>,這是因為 x + 1 的結果(類型為 int)可以隱式轉換為類型 double。

然而,第三個賦值會出現「編譯時錯誤」,這是因為 x 給定的是 double 類型,x + 1 的結果(類型為 double)不能隱式轉換為 int。

第四個賦值成功地把匿名異步函數轉換為委托類型 Func<int, Task<int>>,這是因為 x + 1 的結果(類型為 int)可以隱式轉換為任務類型 Task<int> 的結果類型 int。

匿名函數可能會影響重載策略(overload resolution),并參與類型推斷(type inference),關于這一點請參見第七章第五節。

匿名函數轉換為委托類型的計算

匿名函數到委托類型的轉換將產生委托實例,這個委托實例引用匿名函數以及被捕獲的處于活動狀態的外部變量的集合(可以為空)。當委托被調用,將執行匿名函數體。使用委托所引用的被捕獲的外部變量集執行方法主體中的代碼。

自匿名函數所產生的委托的調用列表只含一項,確切目標對象與委托的目標方法并未被明確指定。具體來說,沒有具體指定該委托的目標對象是空(null)、所閉包的函數成員的 this 值,還是其它某個對象。

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

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));        ...    }}

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

匿名函數轉換為表達式樹類型的計算

將匿名函數轉換為表達式樹類型會產生一個表達式樹(expression tree,第四章第六節)。準確地講,匿名函數轉換計算會導致構造對象結構——表示匿名函數本身的結構。表達式樹的精確結構以及創建該目錄樹的確切過程為定義的實現(implementation defined)。

實現舉例

本節從其它 C# 構造的角度描述可能的匿名函數轉換實現方法。此處所描述的實現基于 Microsoft C# 編譯器 所使用的相同原理(same principles),但這不意味著強制實現,也不是唯一實現方式。此處僅簡單介紹到表達式樹的轉換,因為它們的標準語義超出了本規范的大綱范圍。

本節其余部分舉了幾個不同特點匿名函數的例子。在每個例子中,提供了到僅使用其他 C# 構造的代碼的相應轉換。在這些例子中,設標識符 D 表示下面委托類型:

public delegate void D();

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

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

這可以轉換為引用編譯器生成的靜態方法,而匿名方法就在該靜態方法內:

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

在下例中,匿名函數引用 this 實例成員:

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

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

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

在這個例子中,匿名函數捕獲一個本地局部變量:

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

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

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);        }    }}

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

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); };        }    }}

這里,編譯器將對所捕獲的局部變量的每一個語句塊創建了一個類,這樣不同語句塊中的局部變量將具有獨立的生命周期。__Locals2 的實例——編譯器為內部語句塊創建的類——包含局部變量 z 以及引用 __Locals1 的實例字段。__Locals1 的實例——編譯器為外部語句塊創建的類——包含局部變量 y 以及引用包容函數成員 this 的字段。對于這些數據結構,可以通過 __Locals2 的實例來獲得所有被捕獲的外層變量,匿名函數的代碼從而可以實現為該類的實例方法。

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);        }    }}

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


方法組轉換

存在從方法組(method group,第七章第一節)到兼容委托類型的隱式轉換(第六章第一節)。對于給定的委托類型 D 以及歸類為方法組的表達式 E,如果 E 包含至少一個能以其正常形式(第七章第 5.3.1 節)應用于使用 D 的形參類型與修飾符構造的實參列表的方法,則存在從 E 到 D 的隱式轉換。具體為:

從方法組 E 到委托類型 D 的轉換時「編譯時」應用在下面的部分中描述。注意,存在從 E 到 D 的隱式轉換并不保證轉換產生的「編譯時」應用會不帶錯誤地成功。

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

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

下面例子展示了方法組的轉換:

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    }}

對于 d1 的賦值隱式地將方法組 F 轉換為 D1 類型的值。

對于 d2 的賦值展示如何創建到具有派生程度較?。孀儯┑男螀㈩愋秃团缮潭容^大(協變)的返回類型的方法的委托。

對于 d3 的賦值展示當方法不適用時如何不不存在轉換。

對于 d4 的賦值展示方法如何必須以其正常形式應用。

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

與其它隱式和顯式轉換一樣,強制轉換操作符可以用于顯式執行方法組轉換。因此下面這個例子

object obj = new EventHandler(myDialog.OkClick);

可以寫成

object obj = (EventHandler)myDialog.OkClick;

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

方法組轉換的「運行時」運算如下所述:

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

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
国产精品99久久久久久白浆小说| 青青草原一区二区| 亚洲一区二区国产| 51精品在线观看| 国模视频一区二区| 2019中文字幕全在线观看| 色噜噜国产精品视频一区二区| 少妇av一区二区三区| 国产一区二区激情| 国产一区二区在线免费视频| 久久人91精品久久久久久不卡| 国产精品人成电影在线观看| 国产精品成人国产乱一区| 日本精品一区二区三区在线| 8090成年在线看片午夜| 日韩精品免费在线播放| 91在线视频导航| 91社区国产高清| 色无极亚洲影院| 亚洲人成在线免费观看| 国产这里只有精品| 欧美xxxx综合视频| 在线观看国产成人av片| 一本色道久久88综合日韩精品| 91麻豆桃色免费看| 久久久爽爽爽美女图片| 另类色图亚洲色图| 性色av香蕉一区二区| 国产一区二区三区欧美| 国产精品白丝av嫩草影院| 成人xxxx视频| 欧美电影免费在线观看| 欧美最猛性xxxxx免费| 欧美激情精品久久久久久免费印度| 欧美高清第一页| 欧美大片在线影院| 色综合久久88| 精品亚洲国产成av人片传媒| 国内精品一区二区三区四区| 国产精品中文字幕在线| 91a在线视频| 日韩欧美在线观看视频| 久久精品成人一区二区三区| 日本精品久久久久久久| 日本久久中文字幕| 久操成人在线视频| 最近免费中文字幕视频2019| 国产精品无码专区在线观看| 97超级碰在线看视频免费在线看| 91久久在线播放| 日韩美女在线看| 日韩精品一区二区视频| 色樱桃影院亚洲精品影院| 欧亚精品中文字幕| 不卡av在线播放| 欧美性猛交xxxx免费看久久久| 久久精品电影网站| 欧美日韩另类在线| 欧美另类xxx| 国产精品久久精品| 97激碰免费视频| 91色在线观看| 欧美理论电影在线观看| 亚洲毛片在线免费观看| 欧美亚洲国产视频小说| 国产成人福利夜色影视| 亚洲欧美日本伦理| 亚洲成人激情小说| 亚洲va久久久噜噜噜| 国产精品扒开腿做爽爽爽视频| 欧美一区二区三区精品电影| 日韩在线观看免费全集电视剧网站| 国产午夜精品全部视频在线播放| 日韩成人av网| 国产拍精品一二三| 国产精品美女久久久久久免费| 最新国产精品拍自在线播放| 欧美大片免费观看| www.99久久热国产日韩欧美.com| 搡老女人一区二区三区视频tv| 久久综合88中文色鬼| 欧美激情影音先锋| 国内精品久久久久| 国产国语刺激对白av不卡| 日韩av影视综合网| 伊人亚洲福利一区二区三区| 国产亚洲欧美一区| 亚洲www在线观看| 日韩网站免费观看| 97久久超碰福利国产精品…| 亚洲午夜未满十八勿入免费观看全集| 色婷婷av一区二区三区在线观看| 久久久亚洲国产天美传媒修理工| 欧美最猛性xxxx| 91精品综合视频| 欧美在线视频一区| 久久综合九色九九| 亚洲影院在线看| 国产欧美在线播放| 最新69国产成人精品视频免费| 久久久久亚洲精品| 成人在线视频网站| 一级做a爰片久久毛片美女图片| 欧美激情一区二区三区成人| 国产日韩av在线| 国产噜噜噜噜久久久久久久久| 97久久精品人人澡人人爽缅北| 日韩中文字幕国产精品| 国内揄拍国内精品少妇国语| 亚洲欧美一区二区三区久久| 久久久这里只有精品视频| 国产精品三级网站| 国产精品丝袜高跟| 亚洲欧美国产精品久久久久久久| 国内精品视频久久| 国产精品久久久久久av| 亚洲人成网在线播放| 国产精品大片wwwwww| 国产精品美女久久久久久免费| 精品视频一区在线视频| 亚洲国产高潮在线观看| 国产精品久久在线观看| 97国产成人精品视频| 国产成人精品一区| 亚洲精品国产精品乱码不99按摩| 国产精品嫩草影院一区二区| 国产主播喷水一区二区| 亚洲高清免费观看高清完整版| 日韩中文有码在线视频| 国产精品jizz在线观看麻豆| 超薄丝袜一区二区| 日韩av在线播放资源| 国产精品精品视频| 久久国产加勒比精品无码| 日韩在线免费视频观看| 国产69精品久久久久9| 欧美激情videos| 久久精品99久久久久久久久| 欧美视频不卡中文| 毛片精品免费在线观看| 久久99亚洲热视| 亚洲福利小视频| 最近2019年日本中文免费字幕| 97在线观看免费高清| 国产精品中文久久久久久久| 在线丨暗呦小u女国产精品| 久久精品人人爽| 久久成人在线视频| 久久久久久久久国产精品| 亚洲精品日韩激情在线电影| 中文字幕日韩av综合精品| 欧美亚洲国产日韩2020| 欧美在线视频在线播放完整版免费观看| 日韩高清免费观看| 91视频国产一区| 国产精品亚洲片夜色在线| 一本色道久久综合狠狠躁篇的优点| 国产精品成人一区二区| 亚洲精品成人免费| 成人免费午夜电影| 中文字幕av一区二区| 国产成人综合一区二区三区| 国产在线不卡精品| 国产精品一区二区性色av|