共用方式為


Visual Basic 中的轉換

轉換是將值從某個類型變更為另一種類型的程式。 例如,型Integer別的值可以轉換成 型別的值,或者型Derived別的值可以轉換成 型DoubleBase別的值,假設 BaseDerived 都是類別且Derived繼承自 Base。 轉換可能不需要值本身變更(如后一個範例所示),或可能需要值表示的重大變更(如先前範例所示)。

轉換可能正在擴大或縮小。 擴大轉換是從類型轉換成另一個類型,其值域至少比原始類型的值定義域大,如果不是大。 擴大轉換絕不會失敗。 縮小轉換是從型別轉換成另一個型別,其值域小於原始類型的值定義域,或與執行轉換時必須採取足夠的無關性(例如,從轉換為 IntegerString時)。 縮小轉換可能需要遺失資訊,可能會失敗。

識別轉換(亦即從型別轉換成本身)和預設值轉換(亦即轉換的來源 Nothing)會針對所有類型定義。

隱式和顯式轉換

轉換可以是 隱含明確。 隱含轉換在沒有任何特殊語法的情況下發生。 以下是將值隱含轉換成 Integer 值的 Long 範例:

Module Test
    Sub Main()
        Dim intValue As Integer = 123
        Dim longValue As Long = intValue

        Console.WriteLine(intValue & " = " & longValue)
    End Sub
End Module

另一方面,明確轉換需要轉換運算元。 嘗試對沒有轉換運算子的值執行明確的轉換,會導致編譯時間錯誤。 下列範例會使用明確的轉換,將值Integer轉換成 Long 值。

Module Test
    Sub Main()
        Dim longValue As Long = 134
        Dim intValue As Integer = CInt(longValue)

        Console.WriteLine(longValue & " = " & intValue)
    End Sub
End Module

隱含轉換集取決於編譯環境和 Option Strict 語句。 如果使用嚴格的語意,則只會隱含地進行擴大轉換。 如果使用寬鬆的語意,則所有擴大和縮小轉換(換句話說,所有轉換)都可能會隱含發生。

布爾值轉換

雖然 Boolean 不是數值型別,但它確實在數值類型之間進行縮小轉換,就好像是列舉型別一樣。 常值True會轉換成 、 UIntegerULong655354294967295UShort18446744073709551615-1 表示式的常值255Byte,用於 SByteShortInteger、、DecimalLong、、 Single和 。Double 常值 False 會轉換成常值 0。 零數值會轉換成常值 False。 所有其他數值都會轉換成常值 True

從布林值轉換成 String 有一個縮小轉換,轉換成 System.Boolean.TrueStringSystem.Boolean.FalseString。 也有從 到 的縮小轉換StringBoolean:如果字串等於 TrueStringFalseString (在目前的文化特性中不區分大小寫),則會使用適當的值;否則它會嘗試將字串剖析為數值類型(可能的話為十六進位或八進位,否則為 float),並使用上述規則;否則會擲回 System.InvalidCastException

數值轉換

數值轉換存在於類型 ByteSByteUShort、、UIntegerShort、、Integer、、ULongLongDecimalSingleDouble之間,以及所有列舉型別。 轉換時,列舉型別會被視為其基礎類型。 轉換成列舉型別時,不需要來源值符合列舉型別中定義的值集。 例如:

Enum Values
    One
    Two
    Three
End Enum

Module Test
    Sub Main()
        Dim x As Integer = 5

        ' OK, even though there is no enumerated value for 5.
        Dim y As Values = CType(x, Values)
    End Sub
End Module

數值轉換會在運行時間進行處理,如下所示:

  • 若要將數值型別轉換成較寬的數值類型,值只會轉換成較寬的類型。 從UInteger、 、 、 LongULongIntegerDecimal 轉換成 SingleDouble 的轉換會四捨五入為最Single接近或Double值。 雖然此轉換可能會導致精確度遺失,但永遠不會造成大小損失。

  • 若要將整數型別轉換成另一個整數型別,或從 SingleDoubleDecimal 轉換成整數型別,結果取決於整數溢位檢查是否開啟:

    如果正在檢查整數溢位:

    • 如果來源是整數型別,如果來源自變數在目的型別的範圍內,轉換就會成功。 如果來源自變數超出目的型別的範圍,則轉換會 System.OverflowException 擲回例外狀況。

    • 如果來源為 SingleDoubleDecimal,則來源值會向上或向下四捨五入為最接近的整數值,而這個整數值會變成轉換的結果。 如果來源值同樣接近兩個整數值,該值會四捨五入為具有最小有效位數位置之偶數的值。 如果產生的整數值超出目的型別的範圍, System.OverflowException 則會擲回例外狀況。

    如果未檢查整數溢位:

    • 如果來源是整數型別,轉換一律會成功,而且只會由捨棄來源值最重要的位所組成。

    • 如果來源為 SingleDoubleDecimal,則轉換一律會成功,而且只會包含將來源值四捨五入到最接近整數值。 如果來源值同樣接近兩個整數值,值一律會四捨五入為具有最小有效位數位置之偶數的值。

  • 針對從 Double 轉換為 Single的轉換,值 Double 會四捨五入為最 Single 接近的值。 Double如果值太小而無法表示為 Single,則結果會變成正零或負零。 Double如果值太大而無法表示為 Single,則結果會變成正無限大或負無限大。 Double如果值為 NaN,則結果也是 NaN

  • 針對從 SingleDouble 轉換成 Decimal的轉換,來源值會轉換成 Decimal 表示,並視需要捨入到第 28 個小數字之後最接近的數位。 如果來源值太小而無法表示為 Decimal,則結果會變成零。 如果來源值為 NaN、無限大或太大而無法表示為 DecimalSystem.OverflowException 則會擲回例外狀況。

  • 針對從 Double 轉換為 Single的轉換,值 Double 會四捨五入為最 Single 接近的值。 Double如果值太小而無法表示為 Single,則結果會變成正零或負零。 Double如果值太大而無法表示為 Single,則結果會變成正無限大或負無限大。 Double如果值為 NaN,則結果也是 NaN

參考轉換

參考型別可以轉換成基底類型,反之亦然。 如果轉換的值是 Null 值、衍生型別本身或衍生型別,則從基底型別轉換成較衍生型別只有在運行時間才會成功。

類別和介面類型可以轉換至任何介面類型,以及從任何介面類型轉換。 如果涉及的實際類型具有繼承或實作關聯性,類型與介面類型之間的轉換只會在運行時間成功。 因為介面類型一律會包含衍生自 Object的類型實例,因此介面類型也可以一律轉換成 和 從 Object轉換。

注意。 將類別轉換成 NotInheritable 和從其未實作的介面不是錯誤,因為代表 COM 類別的類別在運行時間之前可能具有未知的介面實作。

如果參考轉換在運行時間失敗, System.InvalidCastException 則會擲回例外狀況。

參考變異數轉換

泛型介面或委派可能會有變體類型參數,允許在類型的相容變體之間進行轉換。 因此,在運行時間,從類別類型或介面類型轉換成與它繼承自 介面類型或實作之介面類型相容的介面類型將會成功。 同樣地,委派類型也可以轉換至與變體相容的委派類型。 例如,委派類型

Delegate Function F(Of In A, Out R)(a As A) As R

會允許從 F(Of Object, Integer) 轉換成 F(Of String, Integer)。 也就是說,採用 Object 的委派F可以安全地當做接受 String的委派F使用。 叫用委派時,目標方法會預期物件,而字串是物件。

泛型委派或介面類型S(Of S1,...,Sn)在下列狀況下表示與泛型介面或委派類型T(Of T1,...,Tn)相容

  • ST 都是從相同的泛型型別 U(Of U1,...,Un)建構。

  • 針對每個類型參數 Ux

    • 如果型別參數宣告為沒有變異數,則 SxTx 必須是相同的類型。

    • 如果宣告 In 類型參數,則必須有擴大的識別、預設、參考、陣列或類型參數從轉換成 SxTx

    • 如果宣告 Out 類型參數,則必須有擴大的識別、預設、參考、陣列或類型參數從轉換成 TxSx

當從類別轉換成具有 Variant 類型參數的泛型介面時,如果類別實作多個 Variant 相容介面,則轉換若沒有非變體轉換,則轉換會模棱兩可。 例如:

Class Base
End Class

Class Derived1
    Inherits Base
End Class

Class Derived2
    Inherits Base
End Class

Class OneAndTwo
    Implements IEnumerable(Of Derived1)
    Implements IEnumerable(Of Derived2)
End Class

Class BaseAndOneAndTwo
    Implements IEnumerable(Of Base)
    Implements IEnumerable(Of Derived1)
    Implements IEnumerable(Of Derived2)
End Class

Module Test
    Sub Main()
        ' Error: conversion is ambiguous
        Dim x As IEnumerable(Of Base) = New OneAndTwo()

        ' OK, will pick up the direct implementation of IEnumerable(Of Base)
        Dim y as IEnumerable(Of Base) = New BaseAndOneAndTwo()
    End Sub
End Module

匿名委派轉換

當分類為 Lambda 方法的表達式在沒有目標類型的內容中重新分類為值時, Dim x = Function(a As Integer, b As Integer) a + b或目標類型不是委派型別時,產生的表達式類型是相當於 Lambda 方法簽章的匿名委派類型。 這個匿名委派類型會轉換成任何相容的委派類型:相容的委派類型是任何委派類型,可以使用委派建立表達式搭配匿名委派類型的 Invoke 方法做為參數來建立。 例如:

' Anonymous delegate type similar to Func(Of Object, Object, Object)
Dim x = Function(x, y) x + y

' OK because delegate type is compatible
Dim y As Func(Of Integer, Integer, Integer) = x

請注意,型 System.Delegate 別和 System.MulticastDelegate 本身並不被視為委派類型(即使所有委派類型都繼承自這些類型也一樣)。 此外,請注意,從匿名委派類型轉換成相容的委派類型不是參考轉換。

陣列轉換

除了在數位上定義的轉換,因為它們是參考型別,陣列也有數個特殊的轉換。

對於任兩種型A別和 B,如果兩者都是值型別未知的參考型別或型別參數,而且如果 A 具有參考、陣列或型別參數轉換成 ,則從型A別的數位轉換成B具有相同等級的類型B數位。 此關聯性稱為 陣列共變數。 數位共變數特別表示陣列的元素,其專案型別實際上可能是元素型 B 別為 A的陣列元素,前提是 AB 都是參考型別,而且 B 具有參考轉換或數位轉換至 A的專案。 在下列範例中,第二次叫用F會導致擲回例外狀況,因為的實際項目類型bString,而不是 ObjectSystem.ArrayTypeMismatchException

Module Test
    Sub F(ByRef x As Object)
    End Sub

    Sub Main()
        Dim a(10) As Object
        Dim b() As Object = New String(10) {}
        F(a(0)) ' OK.
        F(b(1)) ' Not allowed: System.ArrayTypeMismatchException.
   End Sub
End Module

由於陣列共變數,參考型別數位元素的指派包含運行時間檢查,以確保指派給陣列元素的值實際上是允許的類型。

Module Test
    Sub Fill(array() As Object, index As Integer, count As Integer, _
            value As Object)
        Dim i As Integer

        For i = index To (index + count) - 1
            array(i) = value
        Next i
    End Sub

    Sub Main()
        Dim strings(100) As String

        Fill(strings, 0, 101, "Undefined")
        Fill(strings, 0, 10, Nothing)
        Fill(strings, 91, 10, 0)
    End Sub
End Module

在此範例中,方法中的 Fill 指派array(i)會隱含包含運行時間檢查,以確保變數value所參考的物件為 Nothing 或與陣列array實際項目類型相容的實例。 在方法中,方法 Main的前兩個調用 Fill 會成功,但第三個調用會在執行第一個 System.ArrayTypeMismatchException 指派 array(i)給 時擲回例外狀況。 因為 無法儲存在陣列中,所以 Integer 會發生例外狀況 String

如果其中一個數位專案類型是類型參數,其類型在運行時間會變成實值型別, System.InvalidCastException 則會擲回例外狀況。 例如:

Module Test
    Sub F(Of T As U, U)(x() As T)
        Dim y() As U = x
    End Sub

    Sub Main()
        ' F will throw an exception because Integer() cannot be
        ' converted to Object()
        F(New Integer() { 1, 2, 3 })
    End Sub
End Module

如果陣列具有相同的等級,則列舉型別的陣列和列舉型別基礎型別的數位,或具有相同基礎類型的另一個列舉型別數位之間也存在轉換。

Enum Color As Byte
    Red
    Green
    Blue
End Enum

Module Test
    Sub Main()
        Dim a(10) As Color
        Dim b() As Integer
        Dim c() As Byte

        b = a    ' Error: Integer is not the underlying type of Color
        c = a    ' OK
        a = c    ' OK
    End Sub
End Module

在此範例中,的陣列Color會從 、 的基礎型別數位來ByteColor回轉換。 不過,轉換成 的 Integer陣列將會是錯誤,因為 Integer 不是的基礎型別 Color

A()別的 rank-1 陣列也有陣列轉換至集合介面類型 IList(Of B)IReadOnlyList(Of B)ICollection(Of B)IReadOnlyCollection(Of B)IEnumerable(Of B) 中找到System.Collections.Generic,只要下列其中一項成立:

  • AB 都是未知為實值型別的參考型別或型別參數;而且 A 具有擴大的參考、陣列或型別參數轉換成 B; 或
  • AB 都是相同基礎型別的列舉型別;或
  • 其中 A 一個 是 B 列舉型別,另一個是其基礎類型。

具有任何順位的任何類型 A 陣列,也會將陣列轉換成非泛型集合介面類型 IListICollection 並在 IEnumerable 中找到 System.Collections

您可以使用 來逐一查看產生的介面,或透過直接叫GetEnumeratorFor Each方法。 如果排名 1 陣列已轉換泛型或非泛型形式的 IListICollection,您也可以依索引取得元素。 在轉換成泛型或非泛型形式的 IList排名 1 陣列的情況下,也可以依索引設定元素,但受限於上述相同的運行時間陣列共變數檢查。 所有其他介面方法的行為均未由 VB 語言規格定義;它由基礎運行時間決定。

實值類型轉換

實值型別值可以轉換成其基底參考型別之一,或是透過稱為 Boxing 的進程實作的介面類型。 將實值類型值 boxed 時,值會從其所在的位置複製到 .NET Framework 堆積。 接著會傳回堆積上這個位置的參考,並可以儲存在參考類型變數中。 這個參考也稱為實值型別的 Boxed 實例。 Boxed 實例的語意與參考型別相同,而不是實值型別。

Boxed 實值型別可以透過稱為 Unboxing 的程式,轉換回其原始實值型別。 取消 Boxed 實值類型時,值會從堆積複製到變數位置。 從那一點開始,它的行為就好像是實值型別一樣。 將實值型別取消收件匣時,值必須是 Null 值或實值類型的實例。 否則 System.InvalidCastException 會擲回例外狀況。 如果值是列舉型別的實例,該值也可以取消收件匣到列舉型別的基礎型別,或具有相同基礎類型的另一個列舉型別。 Null 值會被視為常值 Nothing

為了妥善支援可為 Null 的實值型別,在進行 Boxing 和 unboxing 時,會特別處理實值型 System.Nullable(Of T) 別。 如果值的 屬性為 ,則型Nullable(Of T)別的 Boxing 值會產生型T別的 Boxed 值,如果值HasValue的屬性為 TrueFalse,則為 的值。NothingHasValue 將類型的T值取消收箱,以Nullable(Of T)產生 其 Value 屬性為 Boxed 值且 HasValue 其 屬性為 True的實例Nullable(Of T)。 值Nothing可以針對任何 T 取消收件匣,Nullable(Of T)併產生其 HasValue 屬性為 False的值。 因為 Boxed 實值型別的行為就像參考型別,所以可以建立相同值的多個參考。 對於基本型別和列舉型別而言,這無關緊要,因為這些類型的實例是不 可變的。 也就是說,無法修改這些類型的 Boxed 實例,因此無法觀察相同值有多個參考的事實。

另一方面,如果可存取其實例變數,或是其方法或屬性修改其實例變數,結構可能會變動。 如果使用 Boxed 結構的一個參考來修改結構,則 Boxed 結構的所有參考都會看到變更。 由於此結果可能會非預期,因此當型別為 Object 的值從某個位置複製到另一個 Boxed 實值型別時,會自動在堆積上複製,而不只是複製其參考。 例如:

Class Class1
    Public Value As Integer = 0
End Class

Structure Struct1
    Public Value As Integer
End Structure

Module Test
    Sub Main()
        Dim val1 As Object = New Struct1()
        Dim val2 As Object = val1

        val2.Value = 123

        Dim ref1 As Object = New Class1()
        Dim ref2 As Object = ref1

        ref2.Value = 123

        Console.WriteLine("Values: " & val1.Value & ", " & val2.Value)
        Console.WriteLine("Refs: " & ref1.Value & ", " & ref2.Value)
    End Sub
End Module

程式輸出為:

Values: 0, 123
Refs: 123, 123

局部變數欄位的指派不會影響局部變數val2val1的欄位,因為當 Boxed Struct1 指派給 val2時,就會建立值的複本。 相反地,指派ref2.Value = 123會影響 和 ref2 參考的物件ref1

注意。 無法針對具類型的 System.ValueType Boxed 結構進行結構複製,因為無法延遲系結。System.ValueType

在指派時,將複製 Boxed 實值型別的規則有一個例外。 如果 Boxed 實值型別參考儲存在另一個類型內,則不會複製內部參考。 例如:

Structure Struct1
    Public Value As Object
End Structure

Module Test
    Sub Main()
        Dim val1 As Struct1
        Dim val2 As Struct1

        val1.Value = New Struct1()
        val1.Value.Value = 10

        val2 = val1
        val2.Value.Value = 123
        Console.WriteLine("Values: " & val1.Value.Value & ", " & _
            val2.Value.Value)
    End Sub
End Module

程式輸出為:

Values: 123, 123

這是因為複製值時不會複製內部 Boxed 值。 因此, val1.Valueval2.Value 都有相同 Boxed 實值型別的參考。

注意。 未複製內部 Boxed 實值型別是 .NET 類型系統的一項限制,以確保每當複製類型的 Object 值時,所有內部 Boxed 實值型別都會複製, 會非常昂貴。

如先前所述,Boxed 實值型別只能將其原始類型取消收件匣。 不過,當型別為 Object時,會特別處理 Boxed 基本類型。 它們可以轉換成任何其他基本類型,並加以轉換。 例如:

Module Test
    Sub Main()
        Dim o As Object = 5
        Dim b As Byte = CByte(o)  ' Legal
        Console.WriteLine(b) ' Prints 5
    End Sub
End Module

一般而言,Boxed Integer5 無法解壓縮到 Byte 變數中。 不過,由於 IntegerByte 是基本類型且具有轉換,因此允許轉換。

請務必注意,將實值型別轉換成介面與限制為介面的泛型自變數不同。 當存取限制型別參數上的介面成員時, ObjectBoxing 不會發生,因為實值型別會轉換成介面,而且存取介面成員時也一樣。 例如,假設介面 ICounter 包含可用來修改值的方法 Increment 。 如果使用 ICounter 做為條件約束,則會使用呼叫的變數Increment參考來呼叫 方法的Increment實作,而不是 Boxed 複本:

Interface ICounter
    Sub Increment()
    ReadOnly Property Value() As Integer
End Interface

Structure Counter
    Implements ICounter

    Dim _value As Integer

    Property Value() As Integer Implements ICounter.Value
        Get
            Return _value
        End Get
    End Property

    Sub Increment() Implements ICounter.Increment
       value += 1
    End Sub
End Structure

Module Test
      Sub Test(Of T As ICounter)(x As T)
         Console.WriteLine(x.value)
         x.Increment()                     ' Modify x
         Console.WriteLine(x.value)
         CType(x, ICounter).Increment()    ' Modify boxed copy of x
         Console.WriteLine(x.value)
      End Sub

      Sub Main()
         Dim x As Counter
         Test(x)
      End Sub
End Module

第一次呼叫 ,修改 Increment 變數 x中的值。 這與第二次呼叫 Increment 不相等,因為它修改了 x 的封裝複本中的值。 因此,程序的輸出為:

0
1
1

可為 Null 的實值類型轉換

實值型 T 別可以與型 T?別的可為 Null 版本轉換和轉換。 如果正在轉換的值是 Nothing,則從 T?T 轉換成 會擲回System.InvalidOperationException例外狀況。 此外,T?如果 T 具有對S的內建轉換,則具有S型別的轉換。 如果 S 是實值型別,則和 S?之間T?會存在下列內部轉換:

  • 將相同分類轉換(縮小或擴大)從 T? 轉換為 S?

  • 將相同分類轉換(縮小或擴大)從 T 轉換為 S?

  • S? 縮小到 T的轉換。

例如,內建的擴大轉換從 到 存在Integer?Long?,因為內部擴大轉換存在從 IntegerLong

Dim i As Integer? = 10
Dim l As Long? = i

當從 T?S?轉換為 時,如果 的值 T?Nothing,則 的值 S? 會是 Nothing。 當從 S?T 轉換為 或 時,如果 或 S?ST? 的值T?NothingSystem.InvalidCastException則會擲回例外狀況。

因為基礎型 System.Nullable(Of T)別的行為,所以當可為 Null 的實值型 T? 別為 Boxed 時,結果會是型別的 Boxed 值 T,而不是型別 T?的 Boxed 值。 相反地,當取消箱到可為 Null 的實值型 T?別時,值會由 System.Nullable(Of T)包裝,而且 Nothing 會取消收件匣為 類型的 T?Null 值。 例如:

Dim i1? As Integer = Nothing
Dim o1 As Object = i1

Console.WriteLine(o1 Is Nothing)                    ' Will print True
o1 = 10
i1 = CType(o1, Integer?)
Console.WriteLine(i1)                               ' Will print 10

此行為的副作用是,可為 Null 的實值型 T? 別似乎實作 的所有介面 T,因為將實值型別轉換成介面需要 Boxed 型別。 因此, T? 可轉換成可轉換成的所有介面 T 。 不過,請務必注意,可為 Null 的實值型 T? 別實際上不會針對泛型條件約束檢查或反映目的實作 的介面 T 。 例如:

Interface I1
End Interface

Structure T1
    Implements I1
    ...
End Structure

Module Test
    Sub M1(Of T As I1)(ByVal x As T)
    End Sub

    Sub Main()
        Dim x? As T1 = Nothing
        Dim y As I1 = x                ' Valid
        M1(x)                          ' Error: x? does not satisfy I1 constraint
    End Sub
End Module

字串轉換

轉換成CharString字串的結果,其第一個字元是字元值。 轉換成StringChar結果,其值是字串的第一個字元。 將的陣列 Char 轉換成 String 字串,其字元為陣列的元素。 轉換成 StringChar 陣列,會產生其專案為字串字元的字元陣列。

和 、、UIntegerShortUShortSByteByteDecimalSingleLongULongIntegerDoubleDate和 之間的確切轉換String超出此規格的範圍,而且實作相依於一個詳細數據除外。Boolean 字串轉換一律會考慮運行時間環境的目前文化特性。 因此,必須在運行時間執行它們。

擴大轉換

擴大轉換永遠不會溢位,但可能需要失去精確度。 下列轉換正在擴大轉換:

身分識別/預設轉換

  • 從類型到本身。

  • 從針對 Lambda 方法產生的匿名委派類型,重新分類為具有相同簽章的任何委派類型。

  • 從常值 Nothing 到型別。

數值轉換

  • ByteUShortShortUInteger、、IntegerULongLongDecimalSingle、 或 Double

  • SByteShortIntegerLongDecimalSingleDouble

  • UShortUIntegerIntegerULongLongDecimalSingleDouble

  • ShortIntegerLongDecimalSingleDouble

  • UIntegerULongLongDecimalSingleDouble

  • IntegerLongDecimalSingleDouble

  • ULongDecimalSingleDouble

  • LongDecimalSingleDouble

  • DecimalSingleDouble

  • SingleDouble

  • 從常值 0 到列舉型別。 (注意。0 轉換為任何列舉型別的轉換正在擴大,以簡化測試旗標。例如,如果 Values 是具有值的One列舉型別,您可以說 .(v And Values.One) = 0) 來測試類型的Values變數v

  • 從列舉型別到其基礎數值類型,或從其基礎數值類型到其基礎數值型別的數值型別,其基礎數值類型的轉換。

  • 從類型 ULongLong、、UIntegerIntegerUShort、、、 ShortByte或的SByte常數表達式到較窄的類型,前提是常數表達式的值在目的型別的範圍內。 (注意。UIntegerInteger 轉換為 SingleULongLongSingleDouble或 ,或 DecimalSingleDouble 或可能會導致精確度遺失,但永遠不會造成大小損失。另一個擴大的數位轉換永遠不會遺失任何資訊。

參考轉換

  • 從參考型別到基底型別。

  • 從參考型別到介面類型,前提是型別會實作介面或變體相容介面。

  • 從介面類型到 Object

  • 從介面類型到變體相容的介面類型。

  • 從委派類型到 Variant 相容的委派類型。 (注意。 這些規則會隱含許多其他參考轉換。例如,匿名委派是繼承自 System.MulticastDelegate的參考型別;陣列類型是繼承自 System.Array的參考型別;匿名型別是繼承自 System.Object的參考型別。

匿名委派轉換

  • 從針對 Lambda 方法產生的匿名委派類型,重新分類至任何更廣泛的委派類型。

陣列轉換

  • 從具有元素類型的陣列型態SSe到具有元素類型的Te陣列型別T,前提是下列所有專案都成立:

    • ST 只有在項目類型中才不同。

    • Te 都是Se參考型別,或是已知為參考型別的類型參數。

    • 範圍擴大的參考、陣列或類型參數轉換從 到TeSe

  • 從具有列舉項目類型的陣列類型S到具有元素類型的Se數位類型TTe,前提是下列所有專案都成立:

    • ST 只有在項目類型中才不同。

    • Te 是的基礎型別 Se

  • 從具有列舉項目類型 之排名 1 的數位類型SSe,到 System.Collections.Generic.IList(Of Te)、、IReadOnlyList(Of Te)ICollection(Of Te)IReadOnlyCollection(Of Te)、 和 IEnumerable(Of Te),前提是下列其中一項為 true:

    • SeTe 都是參考型別,或是已知為參考型別的類型參數,而擴大的參考、陣列或型別參數轉換則從 SeTe; 或

    • Te 是的基礎型別 Se;或

    • Te 與相同 Se

實值類型轉換

  • 從實值型別到基底型別。

  • 從實作型別到型別的介面類型。

可為 Null 的實值類型轉換

  • 從型 T 別到 型別 T?

  • 從型別到 類型T?S?,其中從類型T到型別S有擴大的轉換。

  • 從型別到 類型TS?,其中從類型T到型別S有擴大的轉換。

  • 從類型 T? 到類型實作的 T 介面類型。

字串轉換

  • CharString

  • Char()String

類型參數轉換

  • 從類型參數到 Object

  • 從類型參數到介面類型條件約束或任何與介面類型條件約束相容的介面變體。

  • 從類型參數到類別條件約束所實作的介面。

  • 從類型參數到與類別條件約束所實作之介面相容的介面變體。

  • 從類型參數到類別條件約束,或類別條件約束的基底類型。

  • 從類型參數到類型參數 T 條件約束 Tx,或任何專案 Tx 都有擴大的轉換。

縮小轉換

縮小轉換是無法證明一律成功的轉換、已知可能遺失信息的轉換,以及不同類型網域的轉換,以利縮小表示法。 下列轉換會分類為縮小轉換:

布爾值轉換

  • BooleanByteSByteUShortShortUIntegerIntegerULongLongDecimalSingle或 。Double

  • ByteSByteUShort、、ShortUIntegerULongIntegerDecimalSingleLong、 或 Double 到 。Boolean

數值轉換

  • ByteSByte

  • SByteByteUShortUIntegerULong

  • UShortByteSByteShort

  • ShortByteSByteUShortUIntegerULong

  • UIntegerByteSByteUShortShortInteger

  • IntegerByteSByteUShortShortUIntegerULong

  • ULongByteSByteUShortShortUIntegerIntegerLong

  • LongByteSByteUShortShortUIntegerIntegerULong

  • DecimalByteSByteUShortShortUIntegerIntegerULongLong

  • SingleByteSByteUShort、、ShortUIntegerIntegerULongLong、 或 Decimal

  • DoubleByteSByteUShortShortUIntegerIntegerULongLongDecimal、 或 。Single

  • 從數值型別到列舉型別。

  • 從列舉型別到其基礎數值型別的數值類型,其基礎數值型別的轉換範圍會縮小。

  • 從列舉型別到另一個列舉型別。

參考轉換

  • 從參考型別到衍生型別。

  • 從類別類型到介面類型,前提是類別類型不會實作介面類型或與其相容的介面類型變體。

  • 從介面類型到類別類型。

  • 從介面類型到另一個介面類型,前提是兩種類型之間沒有繼承關聯性,而且前提是它們與變體不相容。

匿名委派轉換

  • 從針對 Lambda 方法產生的匿名委派類型,重新分類為任何較窄的委派類型。

陣列轉換

  • 從具有元素類型的陣列型別S,到具有專案類型的Se陣列類型TTe,前提是下列所有專案都成立:

    • ST 只有在項目類型中才不同。
    • SeTe 都是參考型別,或是未知為實值型別的類型參數。
    • 範圍縮小的參考、陣列或類型參數轉換從 到TeSe
  • 從具有專案型別的陣列類型SSe到具有列舉專案類型的數位類型TTe,前提是下列所有專案皆為 true:

    • ST 只有在項目類型中才不同。
    • Se 是的基礎型 Te 別,或者它們都是共用相同基礎型別的不同列舉型別。
  • 從具有列舉項目類型 之排名 1 的陣列類型SSe,到 IList(Of Te)、、IReadOnlyList(Of Te)ICollection(Of Te)IReadOnlyCollection(Of Te)IEnumerable(Of Te),前提是下列其中一項為 true:

    • SeTe 都是參考型別,或是已知為參考型別的類型參數,而縮小參考、陣列或類型參數轉換則從 SeTe; 或
    • Se 是的基礎型 Te別,或者它們都是共用相同基礎型別的不同列舉型別。

實值型別轉換

  • 從參考型別到更衍生的實值型別。

  • 從介面類型到實值型別,前提是實作介面類型。

可為 Null 的實值類型轉換

  • 從型 T? 別到型別 T

  • 從型別到類型 T?S?,其中從類型T到型別有縮小轉換。S

  • 從型別到類型 TS?,其中從類型T到型別有縮小轉換。S

  • 從型 S? 別轉換成 類型 T,其中會將類型 S 轉換成 型別 T

字串轉換

  • StringChar

  • StringChar()

  • StringBoolean 與從 BooleanString

  • 和 、、UShortSByteLongShortULongUIntegerInteger、、、 SingleDecimalDouble之間的String轉換。Byte

  • StringDate 與從 DateString

類型參數轉換

  • Object 到類型參數。

  • 從類型參數到介面類型,前提是型別參數不受該介面限制,或受限於實作該介面的類別。

  • 從介面類型到類型參數。

  • 從類型參數到類別條件約束的衍生型別。

  • 從類型參數到類型參數 T 條件約束 Tx 的任何專案,其轉換範圍會縮小。

類型參數轉換

類型參數的轉換取決於條件約束,如果有的話,請放在它們上。 類型參數 T 一律可以轉換成本身、從 Object、 和 和 到任何介面類型。 請注意,如果型T別在運行時間是實值型別,則TObject從轉換成 或 介面類型會是 Boxing 轉換,而從 Object 或介面類型T轉換成 會是 Unboxing 轉換。 具有類別條件約束 C 的類型參數會定義從型別參數到 C 及其基類的其他轉換,反之亦然。 具有型別參數條件約束的類型參數T會定義轉換至 Tx ,而任何Tx轉換專案皆Tx為 。

數位,其項目類型是具有介面條件約束 I 的類型參數,與元素類型為 I的陣列具有相同的共變數陣列轉換,前提是類型參數也有 Class 或類別條件約束(因為只有參考數位元素類型可以是covariant)。 具有類別條件約束 C 之元素型別為型別參數的陣列,與元素類型為 C的陣列具有相同的共變數陣列轉換。

上述轉換規則不允許從不受限制的類型參數轉換成非介面類型,這可能令人吃驚。 之所以如此,是為了防止混淆這類轉換的語意。 例如,請考慮下列宣告:

Class X(Of T)
    Public Shared Function F(t As T) As Long 
        Return CLng(t)    ' Error, explicit conversion not permitted
    End Function
End Class

如果允許轉換為 TInteger ,則可能會輕易預期會 X(Of Integer).F(7) 傳回 7L。 不過,這不會,因為只有在編譯時期已知類型為數值時,才會考慮數值轉換。 為了清楚表達語意,必須改為撰寫上述範例:

Class X(Of T)
    Public Shared Function F(t As T) As Long
        Return CLng(CObj(t))    ' OK, conversions permitted
    End Function
End Class

User-Defined 轉換

內部轉換 是由語言所定義的轉換(亦即列在此規格中),而 使用者定義的轉換 則是透過多載 CType 運算符來定義。 在類型之間轉換時,如果沒有適用的內部轉換,則會考慮使用者定義的轉換。 如果來源和目標類型 最特定的 使用者定義轉換,則會使用使用者定義的轉換。 否則,編譯時間錯誤結果。 最特定的轉換是作數「最接近」來源類型的轉換,其結果類型與目標類型「最接近」。 判斷要使用的使用者定義轉換時,會使用最特定的擴大轉換;如果沒有最特定的擴大轉換,則會使用最特定的縮小轉換。 如果沒有最特定的縮小轉換,則轉換未定義,而且會發生編譯時期錯誤。

下列各節將說明如何判斷最特定的轉換。 他們會使用下列詞彙:

如果從型別到型AB別的內建擴大轉換存在,而且既不是AB介面,則 AB包含, 並B包含A

一組類型 中最包含 的類型是一種類型,其中包含集合中的所有其他類型。 如果沒有單一類型包含所有其他類型,則集合沒有最包含的類型。 就直覺而言,最包含的類型是集合中的「最大」類型,這是一種類型,而其他類型都可以透過擴大轉換來轉換。

一組類型中最 包含 的類型是集合中所有其他類型所包含的一種類型。 如果所有其他類型未包含任何單一類型,則集合沒有最包含的類型。 就直覺而言,最包含的類型是集合中的「最小」類型--一種類型,可透過縮小轉換轉換成其他類型。

收集型 T?別的候選使用者定義轉換時,會改用 所 T 定義的使用者定義轉換運算元。 如果轉換成 的型別也是可為 Null 的實值型別,則會提升任何只涉及不可為 Null 實值型別的使用者 T定義轉換運算符。 從 TS 到 的轉換運算子會隨即提升為 T? 從 轉換成 S? ,並視需要轉換成 T?T來評估 ,然後視需要評估使用者定義的轉換運算符 TS 然後視需要轉換成 SS?。 不過,如果轉換的值是 Nothing,則提升轉換運算子會直接轉換成型別為 NothingS?的值。 例如:

Structure S
    ...
End Structure

Structure T
    Public Shared Widening Operator CType(ByVal v As T) As S
        ...
    End Operator
End Structure

Module Test
    Sub Main()
        Dim x As T?
        Dim y As S?

        y = x                ' Legal: y is still null
        x = New T()
        y = x                ' Legal: Converts from T to S
    End Sub
End Module

解析轉換時,使用者定義轉換運算元一律優先於提升轉換運算符。 例如:

Structure S
    ...
End Structure

Structure T
    Public Shared Widening Operator CType(ByVal v As T) As S
        ...
    End Operator

    Public Shared Widening Operator CType(ByVal v As T?) As S?
        ...
    End Operator
End Structure

Module Test
    Sub Main()
        Dim x As T?
        Dim y As S?

        y = x                ' Calls user-defined conversion, not lifted conversion
    End Sub
End Module

在運行時間,評估使用者定義的轉換最多可能需要三個步驟:

  1. 首先,如有必要,值會從來源類型轉換成作數類型。

  2. 然後,會叫用使用者定義的轉換。

  3. 最後,如有必要,使用者定義轉換的結果會轉換成目標類型。

請務必注意,使用者定義轉換的評估絕不會牽涉到一個以上的使用者定義轉換運算元。

最特定的擴大轉換

使用下列步驟,決定兩種類型之間最特定的使用者定義擴展轉換運算符:

  1. 首先,會收集所有候選轉換運算符。 候選轉換運算符是來源類型中使用者定義的擴展轉換運算元,以及目標類型中所有使用者定義的擴大轉換運算元。

  2. 然後,所有不適用的轉換運算符都會從集合中移除。 如果來源類型與作數類型有內部擴展轉換運算元,而且從運算符的結果到目標類型,則轉換運算符適用於來源類型和目標類型。 如果沒有適用的轉換運算符,則沒有最特定的擴大轉換。

  3. 然後,決定適用轉換運算符的最特定來源類型:

    • 如果任何轉換運算子直接從來源類型轉換,則來源類型是最特定的來源類型。

    • 否則,最特定的來源類型是轉換運算子的合併來源類型集合中最包含的類型。 如果找不到最包含的類型,則沒有最特定的擴大轉換。

  4. 然後,決定適用轉換運算符的最特定目標類型:

    • 如果任一轉換運算子直接轉換成目標類型,則目標類型是最特定的目標類型。

    • 否則,最特定的目標類型是轉換運算符之合併目標型別集中最包含的類型。 如果找不到最包含的類型,則沒有最特定的擴大轉換。

  5. 然後,如果只有一個轉換運算符從最特定的來源類型轉換成最特定的目標類型,則這是最特定的轉換運算符。 如果有多個這類運算符存在,則沒有任何最特定的擴大轉換。

最特定的縮小轉換

使用下列步驟來決定兩種類型之間的最特定使用者定義縮小轉換運算子:

  1. 首先,會收集所有候選轉換運算符。 候選轉換運算符是來源類型中的所有使用者定義轉換運算符,以及目標類型中的所有使用者定義轉換運算符。

  2. 然後,所有不適用的轉換運算符都會從集合中移除。 如果來源類型有內部轉換運算符到作數類型,而且從運算符的結果轉換成目標類型,則轉換運算符適用於來源類型和目標類型。 如果沒有適用的轉換運算符,則沒有最特定的縮小轉換。

  3. 然後,決定適用轉換運算符的最特定來源類型:

    • 如果任何轉換運算子直接從來源類型轉換,則來源類型是最特定的來源類型。

    • 否則,如果任一轉換運算符從包含來源類型的類型轉換,則最特定的來源類型是那些轉換運算符之來源型別組合中最包含的類型。 如果找不到最包含的類型,則沒有最特定的縮小轉換。

    • 否則,最特定的來源類型是轉換運算子之合併來源類型集中最包含的類型。 如果找不到最包含的類型,則沒有最特定的縮小轉換。

  4. 然後,決定適用轉換運算符的最特定目標類型:

    • 如果任一轉換運算子直接轉換成目標類型,則目標類型是最特定的目標類型。

    • 否則,如果任一轉換運算符轉換成目標類型所包含的類型,則最特定的目標類型是那些轉換運算符之來源類型組合中最包含的類型。 如果找不到最包含的類型,則沒有最特定的縮小轉換。

    • 否則,最特定的目標類型是轉換運算符之合併目標型別集中最包含的類型。 如果找不到最包含的類型,則沒有最特定的縮小轉換。

  5. 然後,如果只有一個轉換運算符從最特定的來源類型轉換成最特定的目標類型,則這是最特定的轉換運算符。 如果有多個這類運算符存在,則沒有任何最特定的縮小轉換。

原生轉換

數個轉換會分類為 原生轉換 ,因為它們是由 .NET Framework 原生支援。 這些轉換是可透過 使用 DirectCastTryCast 轉換運算元,以及其他特殊行為進行優化的轉換。 分類為原生轉換的轉換包括:識別轉換、預設轉換、參考轉換、陣列轉換、實值類型轉換,以及類型參數轉換。

主要類型

假設有一組型別,在型別推斷等情況下通常必須判斷集合的主要 別。 一組型別的主要類型是由先移除一或多個其他類型沒有隱含轉換的任何類型所決定。 如果此時沒有保留類型,則沒有主要類型。 然後,主要類型是其餘類型中最包含的類型。 如果包含的型別超過一個,則沒有佔主導地位的類型。