轉換是將值從某個類型變更為另一種類型的程式。 例如,型Integer別的值可以轉換成 型別的值,或者型Derived別的值可以轉換成 型DoubleBase別的值,假設 Base 和 Derived 都是類別且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,用於 SByte、Short、Integer、、DecimalLong、、 Single和 。Double 常值 False 會轉換成常值 0。 零數值會轉換成常值 False。 所有其他數值都會轉換成常值 True。
從布林值轉換成 String 有一個縮小轉換,轉換成 System.Boolean.TrueString 或 System.Boolean.FalseString。 也有從 到 的縮小轉換StringBoolean:如果字串等於 TrueString 或 FalseString (在目前的文化特性中不區分大小寫),則會使用適當的值;否則它會嘗試將字串剖析為數值類型(可能的話為十六進位或八進位,否則為 float),並使用上述規則;否則會擲回 System.InvalidCastException。
數值轉換
數值轉換存在於類型 Byte、SByte、UShort、、UIntegerShort、、Integer、、ULong、LongDecimal、Single和 Double之間,以及所有列舉型別。 轉換時,列舉型別會被視為其基礎類型。 轉換成列舉型別時,不需要來源值符合列舉型別中定義的值集。 例如:
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、 、 、LongULongInteger或Decimal轉換成Single或Double的轉換會四捨五入為最Single接近或Double值。 雖然此轉換可能會導致精確度遺失,但永遠不會造成大小損失。若要將整數型別轉換成另一個整數型別,或從
Single、Double或Decimal轉換成整數型別,結果取決於整數溢位檢查是否開啟:如果正在檢查整數溢位:
如果來源是整數型別,如果來源自變數在目的型別的範圍內,轉換就會成功。 如果來源自變數超出目的型別的範圍,則轉換會
System.OverflowException擲回例外狀況。如果來源為
Single、Double或Decimal,則來源值會向上或向下四捨五入為最接近的整數值,而這個整數值會變成轉換的結果。 如果來源值同樣接近兩個整數值,該值會四捨五入為具有最小有效位數位置之偶數的值。 如果產生的整數值超出目的型別的範圍,System.OverflowException則會擲回例外狀況。
如果未檢查整數溢位:
如果來源是整數型別,轉換一律會成功,而且只會由捨棄來源值最重要的位所組成。
如果來源為
Single、Double或Decimal,則轉換一律會成功,而且只會包含將來源值四捨五入到最接近整數值。 如果來源值同樣接近兩個整數值,值一律會四捨五入為具有最小有效位數位置之偶數的值。
針對從
Double轉換為Single的轉換,值Double會四捨五入為最Single接近的值。Double如果值太小而無法表示為Single,則結果會變成正零或負零。Double如果值太大而無法表示為Single,則結果會變成正無限大或負無限大。Double如果值為NaN,則結果也是NaN。針對從
Single或Double轉換成Decimal的轉換,來源值會轉換成Decimal表示,並視需要捨入到第 28 個小數字之後最接近的數位。 如果來源值太小而無法表示為Decimal,則結果會變成零。 如果來源值為NaN、無限大或太大而無法表示為Decimal,System.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)相容:
S和T都是從相同的泛型型別U(Of U1,...,Un)建構。針對每個類型參數
Ux:如果型別參數宣告為沒有變異數,則
Sx且Tx必須是相同的類型。如果宣告
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的陣列元素,前提是 A 和 B 都是參考型別,而且 B 具有參考轉換或數位轉換至 A的專案。 在下列範例中,第二次叫用F會導致擲回例外狀況,因為的實際項目類型b為String,而不是 Object:System.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,只要下列其中一項成立:
-
A和B都是未知為實值型別的參考型別或型別參數;而且A具有擴大的參考、陣列或型別參數轉換成B; 或 -
A和B都是相同基礎型別的列舉型別;或 - 其中
A一個 是B列舉型別,另一個是其基礎類型。
具有任何順位的任何類型 A 陣列,也會將陣列轉換成非泛型集合介面類型 IList, ICollection 並在 IEnumerable 中找到 System.Collections。
您可以使用 來逐一查看產生的介面,或透過直接叫GetEnumerator用For Each方法。 如果排名 1 陣列已轉換泛型或非泛型形式的 IList 或 ICollection,您也可以依索引取得元素。 在轉換成泛型或非泛型形式的 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.Value 和 val2.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 Integer 值 5 無法解壓縮到 Byte 變數中。 不過,由於 Integer 和 Byte 是基本類型且具有轉換,因此允許轉換。
請務必注意,將實值型別轉換成介面與限制為介面的泛型自變數不同。 當存取限制型別參數上的介面成員時, 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?,因為內部擴大轉換存在從 Integer 到 Long:
Dim i As Integer? = 10
Dim l As Long? = i
當從 T?S?轉換為 時,如果 的值 T? 是 Nothing,則 的值 S? 會是 Nothing。 當從 S?T 轉換為 或 時,如果 或 S?ST? 的值T?是 Nothing,System.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 字串,其字元為陣列的元素。 轉換成 String 的 Char 陣列,會產生其專案為字串字元的字元陣列。
和 、、UIntegerShortUShortSByteByte、DecimalSingleLongULongIntegerDouble、 Date和 之間的確切轉換String超出此規格的範圍,而且實作相依於一個詳細數據除外。Boolean 字串轉換一律會考慮運行時間環境的目前文化特性。 因此,必須在運行時間執行它們。
擴大轉換
擴大轉換永遠不會溢位,但可能需要失去精確度。 下列轉換正在擴大轉換:
身分識別/預設轉換
從類型到本身。
從針對 Lambda 方法產生的匿名委派類型,重新分類為具有相同簽章的任何委派類型。
從常值
Nothing到型別。
數值轉換
從
Byte到UShort、Short、UInteger、、Integer、ULongLong、Decimal、Single、 或Double。從
SByte到Short、Integer、Long、Decimal、Single或Double。從
UShort到UInteger、Integer、ULong、Long、Decimal、Single或Double。從
Short到Integer、Long、DecimalSingle或Double。從
UInteger到ULong、Long、Decimal、Single或Double。從
Integer到Long、DecimalSingle或Double。從
ULong到Decimal、Single或Double。從
Long到Decimal,Single或Double。從
Decimal到Single或Double。從
Single到Double。從常值
0到列舉型別。 (注意。從0轉換為任何列舉型別的轉換正在擴大,以簡化測試旗標。例如,如果Values是具有值的One列舉型別,您可以說 .(v And Values.One) = 0) 來測試類型的Values變數v。從列舉型別到其基礎數值類型,或從其基礎數值類型到其基礎數值型別的數值型別,其基礎數值類型的轉換。
從類型
ULong、Long、、UIntegerIntegerUShort、、、ShortByte或的SByte常數表達式到較窄的類型,前提是常數表達式的值在目的型別的範圍內。 (注意。 從UInteger或Integer轉換為Single、ULong或LongSingleDouble或 ,或DecimalSingleDouble或可能會導致精確度遺失,但永遠不會造成大小損失。另一個擴大的數位轉換永遠不會遺失任何資訊。
參考轉換
從參考型別到基底型別。
從參考型別到介面類型,前提是型別會實作介面或變體相容介面。
從介面類型到
Object。從介面類型到變體相容的介面類型。
從委派類型到 Variant 相容的委派類型。 (注意。 這些規則會隱含許多其他參考轉換。例如,匿名委派是繼承自
System.MulticastDelegate的參考型別;陣列類型是繼承自System.Array的參考型別;匿名型別是繼承自System.Object的參考型別。
匿名委派轉換
- 從針對 Lambda 方法產生的匿名委派類型,重新分類至任何更廣泛的委派類型。
陣列轉換
從具有元素類型的陣列型態
SSe到具有元素類型的Te陣列型別T,前提是下列所有專案都成立:S和T只有在項目類型中才不同。和
Te都是Se參考型別,或是已知為參考型別的類型參數。範圍擴大的參考、陣列或類型參數轉換從 到
Te。Se
從具有列舉項目類型的陣列類型
S到具有元素類型的Se數位類型TTe,前提是下列所有專案都成立:S和T只有在項目類型中才不同。Te是的基礎型別Se。
從具有列舉項目類型 之排名 1 的數位類型
SSe,到System.Collections.Generic.IList(Of Te)、、IReadOnlyList(Of Te)ICollection(Of Te)、IReadOnlyCollection(Of Te)、 和IEnumerable(Of Te),前提是下列其中一項為 true:Se和Te都是參考型別,或是已知為參考型別的類型參數,而擴大的參考、陣列或型別參數轉換則從Se到Te; 或Te是的基礎型別Se;或Te與相同Se
實值類型轉換
從實值型別到基底型別。
從實作型別到型別的介面類型。
可為 Null 的實值類型轉換
從型
T別到 型別T?。從型別到 類型
T?S?,其中從類型T到型別S有擴大的轉換。從型別到 類型
TS?,其中從類型T到型別S有擴大的轉換。從類型
T?到類型實作的T介面類型。
字串轉換
從
Char到String。從
Char()到String。
類型參數轉換
從類型參數到
Object。從類型參數到介面類型條件約束或任何與介面類型條件約束相容的介面變體。
從類型參數到類別條件約束所實作的介面。
從類型參數到與類別條件約束所實作之介面相容的介面變體。
從類型參數到類別條件約束,或類別條件約束的基底類型。
從類型參數到類型參數
T條件約束Tx,或任何專案Tx都有擴大的轉換。
縮小轉換
縮小轉換是無法證明一律成功的轉換、已知可能遺失信息的轉換,以及不同類型網域的轉換,以利縮小表示法。 下列轉換會分類為縮小轉換:
布爾值轉換
從
Boolean到Byte、SByte、UShort、ShortUIntegerIntegerULongLongDecimal、Single或 。Double從
Byte、SByte、UShort、、Short、UIntegerULongInteger、DecimalSingleLong、 或Double到 。Boolean
數值轉換
從
Byte到SByte。從
SByte到Byte、UShort、UInteger或ULong。從
UShort到Byte、SByte或Short。從
Short到Byte、SByte、UShort、UInteger或ULong。從
UInteger到Byte、SByte、UShort、Short或Integer。從
Integer到Byte、SByte、UShort、Short、UInteger或ULong。從
ULong到Byte、SByte、UShort、Short、UInteger、Integer或Long。從
Long到Byte、SByte、UShort、Short、UInteger、Integer或ULong。從
Decimal到Byte、SByte、UShort、Short、UInteger、Integer、ULong或Long。從
Single到Byte、SByte、UShort、、Short、UIntegerInteger、ULong、Long、 或Decimal。從
Double到Byte、SByte、UShort、ShortUIntegerIntegerULongLong、Decimal、 或 。Single從數值型別到列舉型別。
從列舉型別到其基礎數值型別的數值類型,其基礎數值型別的轉換範圍會縮小。
從列舉型別到另一個列舉型別。
參考轉換
從參考型別到衍生型別。
從類別類型到介面類型,前提是類別類型不會實作介面類型或與其相容的介面類型變體。
從介面類型到類別類型。
從介面類型到另一個介面類型,前提是兩種類型之間沒有繼承關聯性,而且前提是它們與變體不相容。
匿名委派轉換
- 從針對 Lambda 方法產生的匿名委派類型,重新分類為任何較窄的委派類型。
陣列轉換
從具有元素類型的陣列型別
S,到具有專案類型的Se陣列類型TTe,前提是下列所有專案都成立:-
S和T只有在項目類型中才不同。 -
Se和Te都是參考型別,或是未知為實值型別的類型參數。 - 範圍縮小的參考、陣列或類型參數轉換從 到
Te。Se
-
從具有專案型別的陣列類型
SSe到具有列舉專案類型的數位類型TTe,前提是下列所有專案皆為 true:-
S和T只有在項目類型中才不同。 -
Se是的基礎型Te別,或者它們都是共用相同基礎型別的不同列舉型別。
-
從具有列舉項目類型 之排名 1 的陣列類型
SSe,到IList(Of Te)、、IReadOnlyList(Of Te)ICollection(Of Te)IReadOnlyCollection(Of Te)和IEnumerable(Of Te),前提是下列其中一項為 true:-
Se和Te都是參考型別,或是已知為參考型別的類型參數,而縮小參考、陣列或類型參數轉換則從Se到Te; 或 -
Se是的基礎型Te別,或者它們都是共用相同基礎型別的不同列舉型別。
-
實值型別轉換
從參考型別到更衍生的實值型別。
從介面類型到實值型別,前提是實作介面類型。
可為 Null 的實值類型轉換
從型
T?別到型別T。從型別到類型
T?S?,其中從類型T到型別有縮小轉換。S從型別到類型
TS?,其中從類型T到型別有縮小轉換。S從型
S?別轉換成 類型T,其中會將類型S轉換成 型別T。
字串轉換
從
String到Char。從
String到Char()。從
String到Boolean與從Boolean到String。和 、、
UShortSByteLongShortULongUIntegerInteger、、、SingleDecimal或Double之間的String轉換。Byte從
String到Date與從Date到String。
類型參數轉換
從
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介面,則 A 會B包含, 並B包含A 。
一組類型 中最包含 的類型是一種類型,其中包含集合中的所有其他類型。 如果沒有單一類型包含所有其他類型,則集合沒有最包含的類型。 就直覺而言,最包含的類型是集合中的「最大」類型,這是一種類型,而其他類型都可以透過擴大轉換來轉換。
一組類型中最 包含 的類型是集合中所有其他類型所包含的一種類型。 如果所有其他類型未包含任何單一類型,則集合沒有最包含的類型。 就直覺而言,最包含的類型是集合中的「最小」類型--一種類型,可透過縮小轉換轉換成其他類型。
收集型 T?別的候選使用者定義轉換時,會改用 所 T 定義的使用者定義轉換運算元。 如果轉換成 的型別也是可為 Null 的實值型別,則會提升任何只涉及不可為 Null 實值型別的使用者 T定義轉換運算符。 從 TS 到 的轉換運算子會隨即提升為 T? 從 轉換成 S? ,並視需要轉換成 T?T來評估 ,然後視需要評估使用者定義的轉換運算符 T , S 然後視需要轉換成 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
在運行時間,評估使用者定義的轉換最多可能需要三個步驟:
首先,如有必要,值會從來源類型轉換成作數類型。
然後,會叫用使用者定義的轉換。
最後,如有必要,使用者定義轉換的結果會轉換成目標類型。
請務必注意,使用者定義轉換的評估絕不會牽涉到一個以上的使用者定義轉換運算元。
最特定的擴大轉換
使用下列步驟,決定兩種類型之間最特定的使用者定義擴展轉換運算符:
首先,會收集所有候選轉換運算符。 候選轉換運算符是來源類型中使用者定義的擴展轉換運算元,以及目標類型中所有使用者定義的擴大轉換運算元。
然後,所有不適用的轉換運算符都會從集合中移除。 如果來源類型與作數類型有內部擴展轉換運算元,而且從運算符的結果到目標類型,則轉換運算符適用於來源類型和目標類型。 如果沒有適用的轉換運算符,則沒有最特定的擴大轉換。
然後,決定適用轉換運算符的最特定來源類型:
如果任何轉換運算子直接從來源類型轉換,則來源類型是最特定的來源類型。
否則,最特定的來源類型是轉換運算子的合併來源類型集合中最包含的類型。 如果找不到最包含的類型,則沒有最特定的擴大轉換。
然後,決定適用轉換運算符的最特定目標類型:
如果任一轉換運算子直接轉換成目標類型,則目標類型是最特定的目標類型。
否則,最特定的目標類型是轉換運算符之合併目標型別集中最包含的類型。 如果找不到最包含的類型,則沒有最特定的擴大轉換。
然後,如果只有一個轉換運算符從最特定的來源類型轉換成最特定的目標類型,則這是最特定的轉換運算符。 如果有多個這類運算符存在,則沒有任何最特定的擴大轉換。
最特定的縮小轉換
使用下列步驟來決定兩種類型之間的最特定使用者定義縮小轉換運算子:
首先,會收集所有候選轉換運算符。 候選轉換運算符是來源類型中的所有使用者定義轉換運算符,以及目標類型中的所有使用者定義轉換運算符。
然後,所有不適用的轉換運算符都會從集合中移除。 如果來源類型有內部轉換運算符到作數類型,而且從運算符的結果轉換成目標類型,則轉換運算符適用於來源類型和目標類型。 如果沒有適用的轉換運算符,則沒有最特定的縮小轉換。
然後,決定適用轉換運算符的最特定來源類型:
如果任何轉換運算子直接從來源類型轉換,則來源類型是最特定的來源類型。
否則,如果任一轉換運算符從包含來源類型的類型轉換,則最特定的來源類型是那些轉換運算符之來源型別組合中最包含的類型。 如果找不到最包含的類型,則沒有最特定的縮小轉換。
否則,最特定的來源類型是轉換運算子之合併來源類型集中最包含的類型。 如果找不到最包含的類型,則沒有最特定的縮小轉換。
然後,決定適用轉換運算符的最特定目標類型:
如果任一轉換運算子直接轉換成目標類型,則目標類型是最特定的目標類型。
否則,如果任一轉換運算符轉換成目標類型所包含的類型,則最特定的目標類型是那些轉換運算符之來源類型組合中最包含的類型。 如果找不到最包含的類型,則沒有最特定的縮小轉換。
否則,最特定的目標類型是轉換運算符之合併目標型別集中最包含的類型。 如果找不到最包含的類型,則沒有最特定的縮小轉換。
然後,如果只有一個轉換運算符從最特定的來源類型轉換成最特定的目標類型,則這是最特定的轉換運算符。 如果有多個這類運算符存在,則沒有任何最特定的縮小轉換。
原生轉換
數個轉換會分類為 原生轉換 ,因為它們是由 .NET Framework 原生支援。 這些轉換是可透過 使用 DirectCast 和 TryCast 轉換運算元,以及其他特殊行為進行優化的轉換。 分類為原生轉換的轉換包括:識別轉換、預設轉換、參考轉換、陣列轉換、實值類型轉換,以及類型參數轉換。
主要類型
假設有一組型別,在型別推斷等情況下通常必須判斷集合的主要 型 別。 一組型別的主要類型是由先移除一或多個其他類型沒有隱含轉換的任何類型所決定。 如果此時沒有保留類型,則沒有主要類型。 然後,主要類型是其餘類型中最包含的類型。 如果包含的型別超過一個,則沒有佔主導地位的類型。