変換とは、ある型から別の型に値を変更するプロセスです。 たとえば、Integer型の値をDouble型の値に変換したり、型Derivedの値をBase型の値に変換したりできます。これは、BaseとDerivedの両方がクラスであり、Baseから継承Derivedと見なされます。 変換では、(後者の例のように) 値自体を変更する必要がない場合や、(前の例のように) 値表現を大幅に変更する必要がある場合があります。
変換は拡大または縮小のどちらでもかまいません。
拡大変換とは、型から別の型への変換で、値ドメインが少なくとも元の型の値ドメインよりも大きくても大きくない場合です。 拡大変換が失敗することはありません。
縮小変換とは、型から別の型への変換で、値ドメインが元の型の値ドメインよりも小さいか、変換を行うとき (たとえば、IntegerからStringに変換する場合) に特別な注意を払う必要があることを十分に関係がありません。 情報の損失を伴う可能性がある変換の縮小は失敗する可能性があります。
ID 変換 (つまり、型からそれ自体への変換) と既定値の変換 (つまり、 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
一方、明示的な変換にはキャスト演算子が必要です。 キャスト演算子なしで値に対して明示的な変換を実行しようとすると、コンパイル時エラーが発生します。 次の例では、明示的な変換を使用して、 Long 値を Integer 値に変換します。
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は、Byte、255UShortの65535、UIntegerの4294967295、ULongの18446744073709551615、およびSByte、Short、Integer、Long、Decimal、Single、およびDoubleの式-1に変換されます。 リテラル False はリテラル 0に変換されます。 数値が 0 の場合、リテラル Falseに変換されます。 その他のすべての数値は、リテラル Trueに変換されます。
ブール値から文字列への縮小変換があり、 System.Boolean.TrueString または System.Boolean.FalseStringに変換されます。
StringからBooleanへの縮小変換もあります。文字列がTrueStringまたはFalseString (現在のカルチャでは大文字と小文字を区別せずに) と等しい場合は、適切な値を使用します。それ以外の場合は、文字列を数値型 (可能な場合は 16 進数または 8 進数)として解析し、上記の規則を使用します。それ以外の場合はSystem.InvalidCastExceptionをスローします。
数値変換
数値変換は、 Byte、 SByte、 UShort、 Short、 UInteger、 Integer、 ULong、 Long、 Decimal、 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、Integer、ULong、Long、またはDecimalからSingleまたはDoubleへの変換は、最も近いSingleまたはDouble値に丸められます。 この変換によって精度が失われる可能性はあり、桁違いになることはありません。整数型から別の整数型への変換、または整数型への
Single、Double、またはDecimalからの変換の場合、結果は整数オーバーフロー チェックがオンかどうかによって異なります。整数オーバーフローがチェックされている場合:
ソースが整数型の場合、変換元の引数が変換先の型の範囲内にある場合、変換は成功します。 変換元の引数が変換先の型の範囲外の場合、
System.OverflowException例外がスローされます。ソースが
Single、Double、またはDecimalの場合、ソース値は最も近い整数値に切り上げられたり切り捨てられ、この整数値が変換の結果になります。 ソース値が 2 つの整数値に等しく近い場合、値は最下位桁の偶数の値に丸められます。 結果の整数値が変換先の型の範囲外の場合は、System.OverflowException例外がスローされます。
整数オーバーフローがチェックされていない場合:
ソースが整数型の場合、変換は常に成功し、ソース値の最上位ビットを破棄するだけで構成されます。
ソースが
Single、Double、またはDecimalの場合、変換は常に成功し、ソース値を最も近い整数値に丸めるだけで構成されます。 ソース値が 2 つの整数値に等しく近い場合、値は常に、最下位桁の偶数の値に丸められます。
DoubleからSingleへの変換では、Double値は最も近いSingle値に丸められます。Double値が小さすぎてSingleとして表すと、結果は正のゼロまたは負のゼロになります。Double値が大きすぎてSingleとして表すと、結果は正の無限大または負の無限大になります。Double値がNaN場合、結果もNaN。SingleまたはDoubleからDecimalへの変換では、ソース値はDecimal表現に変換され、必要に応じて小数点以下 28 桁目の後の最も近い数値に丸められます。 ソース値が小さすぎてDecimalとして表すと、結果は 0 になります。 ソース値がNaN、無限大、または大きすぎてDecimalとして表すには、System.OverflowException例外がスローされます。DoubleからSingleへの変換では、Double値は最も近いSingle値に丸められます。Double値が小さすぎてSingleとして表すと、結果は正のゼロまたは負のゼロになります。Double値が大きすぎてSingleとして表すと、結果は正の無限大または負の無限大になります。Double値がNaN場合、結果もNaN。
参照変換
参照型は基本型に変換でき、その逆も可能です。 基本型から派生型への変換は、変換される値が null 値、派生型自体、またはより多くの派生型である場合にのみ、実行時に成功します。
クラスとインターフェイスの型は、任意のインターフェイス型との間でキャストできます。 型とインターフェイス型の間の変換は、関係する実際の型に継承または実装リレーションシップがある場合にのみ、実行時に成功します。 インターフェイス型には常に、 Objectから派生した型のインスタンスが含まれるため、インターフェイス型は常に Objectとの間でキャストすることもできます。
"注: COM クラスを表すクラスには実行時まで不明なインターフェイス実装がある可能性があるため、 NotInheritable クラスを実装しないインターフェイスとの間で変換してもエラーではありません。
実行時に参照変換が失敗すると、 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:型パラメーターが分散なしで宣言された場合、
SxとTxは同じ型である必要があります。型パラメーターが
In宣言されている場合は、SxからTxへの拡張 ID、既定、参照、配列、または型パラメーターの変換が必要です。型パラメーターが
Out宣言されている場合は、TxからSxへの拡張 ID、既定、参照、配列、または型パラメーターの変換が必要です。
バリアント型パラメーターを使用してクラスからジェネリック インターフェイスに変換する場合、クラスが複数のバリアント互換インターフェイスを実装している場合、非バリアント変換がない場合、変換はあいまいになります。 例えば次が挙げられます。
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
匿名デリゲートの変換
ラムダ メソッドとして分類された式が、ターゲット型 (たとえば、 Dim x = Function(a As Integer, b As Integer) a + b) がないコンテキストで値として再分類される場合、またはターゲット型がデリゲート型ではない場合、結果の式の型はラムダ メソッドのシグネチャと同等の匿名デリゲート型になります。 この匿名デリゲート型には、互換性のあるデリゲート型への変換があります。互換性のあるデリゲート型は、匿名デリゲート型の 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の 2 つの型の場合、両方の参照型または型パラメーターが値型と見なされず、Aに参照、配列、または型パラメーターがBに変換されている場合、A型の配列から同じランクを持つB型の配列への変換が存在します。 このリレーションシップは、 配列共分散と呼ばれます。 配列の共分散は、特に、要素型が B 配列の要素が、実際には要素型が A配列の要素である可能性があることを意味します。ただし、 A と B の両方が参照型であり、 B が参照変換または Aへの配列変換を持っている場合です。 次の例では、Fの 2 番目の呼び出しでは、bの実際の要素型がObjectではなくStringされるため、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では、メソッドの最初の 2 つの呼び出しFill成功しますが、3 番目の呼び出しでは、array(i)への最初の割り当ての実行時にSystem.ArrayTypeMismatchException例外がスローされます。 例外は、 Integer を String 配列に格納できないために発生します。
配列要素型の 1 つが型パラメーターであり、その型が実行時に値型であることが判明した場合、 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の配列は、Colorの基になる型Byteの配列との間で変換されます。 ただし、IntegerはColorの基になる型ではないため、Integerの配列への変換はエラーになります。
A()型の rank-1 配列には、System.Collections.Genericで見つかったコレクション インターフェイス型IList(Of B)、IReadOnlyList(Of B)、ICollection(Of B)、IReadOnlyCollection(Of B)、IEnumerable(Of B)への配列変換も含まれます。次のいずれかが当てはまる限りです。
-
AおよびBは、参照型または値型として知られていない型パラメーターの両方です。また、Aには、拡大参照、配列、または型パラメーターからBへの変換があります。 -
AおよびBは、どちらも同じ基になる型の列挙型です。 -
AとBの 1 つが列挙型で、もう 1 つは基になる型です。
任意のランクを持つ A 型の配列にも、System.Collectionsで見つかった非ジェネリック コレクション インターフェイス型IList、ICollection、IEnumerableへの配列変換があります。
For Eachを使用するか、GetEnumerator メソッドを直接呼び出すことによって、結果のインターフェイスを反復処理できます。 rank-1 配列の場合、 IList または ICollectionのジェネリックまたは非ジェネリック形式に変換され、インデックスによって要素を取得することもできます。
IListのジェネリックまたは非ジェネリック形式に変換されたランク 1 配列の場合は、上記と同じランタイム配列共分散チェックに従って、インデックスによって要素を設定することもできます。 他のすべてのインターフェイス メソッドの動作は、VB 言語仕様によって定義されていません。これは基になるランタイムにかかります。
値型の変換
値型の値は、ボックス 化と呼ばれるプロセスを通じて実装される基本参照型またはインターフェイス型のいずれかに変換できます。 値型の値がボックス化されると、値は .NET Framework ヒープに存在する場所からコピーされます。 その後、ヒープ上のこの場所への参照が返され、参照型変数に格納できます。 この参照は、値型の ボックス化された インスタンスとも呼ばれます。 ボックス化されたインスタンスには、値型ではなく参照型と同じセマンティクスがあります。
ボックス化された値型は、 ボックス化解除と呼ばれるプロセスを通じて元の値型に戻すことができます。 ボックス化された値型がボックス化解除されると、値はヒープから変数の場所にコピーされます。 その時点から、値型であるかのように動作します。 値型のボックス化を解除する場合、値は null 値または値型のインスタンスである必要があります。 それ以外の場合は、 System.InvalidCastException 例外がスローされます。 値が列挙型のインスタンスである場合、その値は、列挙型の基になる型または同じ基になる型を持つ別の列挙型にボックス化解除することもできます。 null 値は、リテラル Nothingであるかのように扱われます。
null 許容値型を適切にサポートするために、 System.Nullable(Of T) 値型は、ボックス化とボックス化解除を行うときに特別に扱われます。
Nullable(Of T)型の値をボックス化すると、値のHasValueプロパティがTrueされている場合はT型のボックス化された値が、値のHasValueプロパティがFalse場合はNothingの値になります。
T型の値をボックス化解除してNullable(Of T)すると、Value プロパティがボックス化された値であり、HasValue プロパティがTrueNullable(Of T)のインスタンスになります。
Nothing値は、任意のTに対してNullable(Of T)するようにボックス化解除でき、HasValueプロパティがFalse値になります。 ボックス化された値型は参照型と同様に動作するため、同じ値への複数の参照を作成できます。 プリミティブ型と列挙型の場合、これらの型のインスタンスは 不変であるため、これは関係ありません。 つまり、これらの型のボックス化されたインスタンスを変更することはできないため、同じ値への参照が複数あるという事実を観察することはできません。
一方、構造体は、そのインスタンス変数にアクセスできる場合や、そのメソッドまたはプロパティがそのインスタンス変数を変更する場合に変更可能な場合があります。 ボックス化された構造体への 1 つの参照を使用して構造を変更すると、ボックス化された構造体へのすべての参照に変更が表示されます。 この結果は予期しない可能性があるため、 Object として型指定された値が 1 つの場所から別のボックス化された値型にコピーされると、参照がコピーされるのではなく、ヒープ上で自動的に複製されます。 例えば次が挙げられます。
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
ボックス化されたStruct1がval2に割り当てられたときに値のコピーが行われたため、ローカル変数val2のフィールドへの代入は、ローカル変数val1のフィールドには影響しません。 これに対し、割り当て ref2.Value = 123 は、 ref1 参照と ref2 参照の両方のオブジェクトに影響します。
"注:
System.ValueTypeの遅延バインドができないため、System.ValueTypeとして型指定されたボックス化された構造体に対しては、構造体のコピーは行われません。
割り当て時にボックス化された値型がコピーされるというルールには例外が 1 つあります。 ボックス化された値型参照が別の型内に格納されている場合、内部参照はコピーされません。 例えば次が挙げられます。
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
これは、値のコピー時に内部ボックス化された値がコピーされないためです。 したがって、 val1.Value と val2.Value の両方に、同じボックス化された値型への参照があります。
"注: 内部ボックス化された値型がコピーされないという事実は、.NET 型システムの制限です。つまり、 Object 型の値がコピーされるたびに、すべての内部ボックス化された値型がコピーされるようにするには、非常にコストがかかります。
前述のように、ボックス化された値型は元の型にのみボックス化解除できます。 ただし、ボックス化されたプリミティブ型は、 Objectとして型指定された場合に特別に扱われます。 変換先の他のプリミティブ型に変換できます。 例えば次が挙げられます。
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
通常、ボックス化されたInteger値5Byte変数にボックス化解除できませんでした。 ただし、 Integer と Byte はプリミティブ型であり、変換があるため、変換は許可されます。
値型をインターフェイスに変換することは、インターフェイスに制約されているジェネリック引数とは異なされることに注意してください。 制約付き型パラメーター (または Object でのメソッドの呼び出し) でインターフェイス メンバーにアクセスする場合、値型がインターフェイスに変換され、インターフェイス メンバーにアクセスした場合と同様にボックス化は行われません。 たとえば、インターフェイス ICounter に、値の変更に使用できるメソッド Increment が含まれているとします。
ICounterを制約として使用すると、ボックス化されたコピーではなく、Incrementが呼び出された変数への参照を使用して、Increment メソッドの実装が呼び出されます。
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の 2 回目の呼び出しと同じではありません。 したがって、プログラムの出力は次のようになります。
0
1
1
Null 許容値型変換
T値型は、null 許容バージョンの型 (T?) との間で変換できます。
T? から T への変換では、変換される値がNothingされた場合、System.InvalidOperationException例外がスローされます。 また、T?には、Sへの組み込み変換がある場合TS型への変換があります。
Sが値型の場合は、T?とS?の間に次の組み込み変換が存在します。
同じ分類 (縮小または拡大) を
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またはT?からSに変換すると、T?またはS?の値がNothingされると、System.InvalidCastException例外がスローされます。
基になる型 System.Nullable(Of T)の動作のため、null 許容値型 T? ボックス化されると、結果は T型のボックス化された値になります。 T?型のボックス化された値ではありません。 逆に、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のすべてのインターフェイスを実装しているように見えるということです。 その結果、 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
文字列変換
CharをStringに変換すると、最初の文字が文字値である文字列になります。
StringをCharに変換すると、値が文字列の最初の文字である文字になります。
Charの配列をStringに変換すると、配列の要素である文字を持つ文字列が作成されます。
StringをCharの配列に変換すると、要素が文字列の文字である文字の配列が作成されます。
StringとBoolean、Byte、SByte、UShort、Short、UInteger、Integer、ULong、Long、Decimal、Single、Double、Date、およびその逆の間の正確な変換は、この仕様の範囲外であり、実装は 1 つの詳細を除いて依存します。 文字列変換では、実行時環境の現在のカルチャが常に考慮されます。 そのため、実行時に実行する必要があります。
拡大変換
拡大変換はオーバーフローすることはありませんが、精度が失われる可能性があります。 次の変換は拡大変換です。
ID/既定の変換
型からそれ自体へ。
ラムダ メソッドに対して生成された匿名デリゲート型から、同じシグネチャを持つ任意のデリゲート型に再分類します。
リテラル
Nothingから型へ。
数値変換
ByteからUShort、Short、UInteger、Integer、ULong、Long、Decimal、Single、またはDoubleまで。SByteからShort、Integer、Long、Decimal、SingleまたはDouble。UShortからUInteger、Integer、ULong、Long、Decimal、Single、またはDouble。ShortからInteger、Long、Decimal、Single、またはDoubleまで。UIntegerからULong、Long、Decimal、Single、またはDouble。IntegerからLong、Decimal、Single、または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、UInteger、Integer、UShort、Short、Byte、またはSByte型の定数式から、より狭い型に変換されます。 (注。UIntegerまたはIntegerからSingle、ULong、またはLongからSingleまたはDoubleへの変換、またはDecimalからSingleまたはDoubleへの変換は、精度の損失を引き起こす可能性がありますが、大きさが失われることはありません。他の拡大数値変換では、情報が失われることはありません)。
参照変換
参照型から基本型へ。
参照型からインターフェイス型まで(型がインターフェイスまたはバリアント互換インターフェイスを実装している場合)。
インターフェイスの種類から
Object。インターフェイス型からバリアント互換インターフェイス型へ。
デリゲート型からバリアント互換デリゲート型へ。 (注。 他の多くの参照変換は、これらの規則によって暗黙的に示されます。たとえば、匿名デリゲートは
System.MulticastDelegateから継承する参照型です。配列型はSystem.Arrayから継承する参照型です。匿名型はSystem.Objectから継承する参照型です)。
匿名デリゲートの変換
- ラムダ メソッドに対して生成された匿名デリゲート型から、より広いデリゲート型への再分類。
配列変換
要素型
Seを持つ配列型Sから、要素型Teを持つ配列型Tまで、次のすべてが当てはまる場合。SとTは要素の種類でのみ異なります。SeとTeはどちらも参照型であるか、参照型と呼ばれる型パラメーターです。拡大参照、配列、または型パラメーターの変換は、
SeからTeに存在します。
列挙された要素型を持つ配列型
Sから、要素型Teを持つ配列型TにSe。次のすべてが当てはまる場合。SとTは要素の種類でのみ異なります。Teは、Seの基になる型です。
列挙された要素型
Seを持つランク 1 の配列型Sから、System.Collections.Generic.IList(Of Te)、IReadOnlyList(Of Te)、ICollection(Of Te)、IReadOnlyCollection(Of Te)、およびIEnumerable(Of Te)に、次のいずれかが当てはまる場合。SeとTeの両方が参照型であるか、参照型であることが知られている型パラメーターであり、拡大参照、配列、または型パラメーターの変換は、SeからTeに存在します。Teは、Seの基になる型です。Teは次のコードと同じです。Se
値型の変換
値型から基本型へ。
値型から、型が実装するインターフェイス型まで。
Null 許容値型の変換
型
Tから型T?まで。型
T?から型S?にTから型Sへの拡大変換があります。型
Tから型S?にTから型Sへの拡大変換があります。型
T?から、型T実装するインターフェイス型まで。
文字列変換
CharからString。Char()からString。
型パラメーターの変換
型パラメーターから
Object。型パラメーターからインターフェイス型制約、またはインターフェイス型制約と互換性のあるインターフェイスバリアントまで。
型パラメーターから、クラス制約によって実装されるインターフェイスまで。
型パラメーターから、クラス制約によって実装されるインターフェイスと互換性のあるインターフェイスバリアントまで。
型パラメーターからクラス制約、またはクラス制約の基本型。
型パラメーター
Tから型パラメーター制約Tx、または拡大変換Tx何か。
縮小変換
縮小変換とは、常に成功することが証明できない変換、情報が失われる可能性が知られている変換、および縮小表記のメリットを得るために十分に異なる型のドメイン間の変換です。 次の変換は縮小変換として分類されます。
ブール型変換
BooleanからByte、SByte、UShort、Short、UInteger、Integer、ULong、Long、Decimal、Single、またはDoubleまで。Byte、SByte、UShort、Short、UInteger、Integer、ULong、Long、Decimal、Single、または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、UInteger、Integer、ULong、Long、またはDecimalまで。DoubleからByte、SByte、UShort、Short、UInteger、Integer、ULong、Long、Decimal、またはSingleまで。数値型から列挙型へ。
列挙型から数値型まで、基になる数値型には縮小変換があります。
列挙された型から別の列挙型へ。
参照変換
参照型からより派生型へ。
クラス型からインターフェイス型まで、クラス型に対応するインターフェイス型またはインターフェイス型バリアントが実装されていない場合。
インターフェイス型からクラス型へ。
2 つの型の間に継承関係がなく、バリアント互換性がない場合は、インターフェイス型から別のインターフェイス型へ。
匿名デリゲートの変換
- ラムダ メソッドに対して生成された匿名デリゲート型から、より狭いデリゲート型への再分類。
配列変換
要素型
Seを持つ配列型Sから、要素型Teを持つ配列型Tまで、次のすべてが当てはまる場合。-
SとTは要素の種類でのみ異なります。 -
SeとTeの両方が参照型であるか、値型として認識されていない型パラメーターです。 - 縮小参照、配列、または型パラメーターの変換は、
SeからTeに存在します。
-
要素型
Seを持つ配列型Sから、列挙された要素型Teを持つ配列型Tまで、次のすべてが当てはまる場合。-
SとTは要素の種類でのみ異なります。 -
SeはTeの基になる型です。または、両方とも同じ基になる型を共有する異なる列挙型です。
-
列挙された要素型
SSeを持つランク 1 の配列型から、IList(Of Te)、IReadOnlyList(Of Te)、ICollection(Of Te)、IReadOnlyCollection(Of Te)、IEnumerable(Of Te)まで、次のいずれかが当てはまる場合。-
SeとTeの両方が参照型であるか、参照型であることが知られている型パラメーターであり、SeからTeへの縮小参照、配列、または型パラメーターの変換が存在します。 -
SeはTeの基になる型です。または、両方とも同じ基になる型を共有する異なる列挙型です。
-
値型の変換
参照型から、より派生した値型へ。
インターフェイス型から値型まで、値型がインターフェイス型を実装する場合。
Null 許容値型の変換
型
T?から型T。型
T?から型S?にT型から型Sへの縮小変換があります。型
Tから型S?にT型から型Sへの縮小変換があります。型
S?から型Tに変換します。型Sから型Tへの変換があります。
文字列変換
StringからChar。StringからChar()。StringからBoolean、BooleanからStringまで。StringとByte、SByte、UShort、Short、UInteger、Integer、ULong、Long、Decimal、Single、またはDoubleの間の変換。StringからDate、DateからStringまで。
型パラメーターの変換
Objectから型パラメーターへ。型パラメーターからインターフェイス型まで、型パラメーターがそのインターフェイスに制約されていないか、そのインターフェイスを実装するクラスに制約されていない場合。
インターフェイス型から型パラメーターへ。
型パラメーターからクラス制約の派生型へ。
型パラメーター
Tから、縮小変換を持つ型パラメーター制約Tx。
型パラメーターの変換
型パラメーターの変換は、制約 (存在する場合) によって決定されます。
T型パラメーターは、常に、任意のインターフェイス型との間で、Objectとの間で、それ自体に変換できます。
T型が実行時に値型である場合、TからObjectまたはインターフェイス型への変換はボックス化変換になり、Objectまたはインターフェイス型からTへの変換は、ボックス化解除変換になります。 クラス制約 C を持つ型パラメーターは、型パラメーターから C とその基底クラスへの追加の変換を定義します。その逆も同様です。 型パラメーター制約を持つ型パラメーターTTx、Txへの変換と変換Tx定義します。
要素型がインターフェイス制約 I を持つ型パラメーターである配列は、要素型が I配列と同じ共変配列変換を持ちます。ただし、型パラメーターに Class 制約またはクラス制約がある場合 (参照配列要素型のみが共変になるため)。 要素型がクラス制約を持つ型パラメーターである配列 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
TからIntegerへの変換が許可されている場合、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 演算子をオーバーロードすることによって定義されます。 型間で変換する場合、組み込み変換が適用されない場合は、ユーザー定義の変換が考慮されます。 ソースとターゲットの型に 最も固有 のユーザー定義変換がある場合は、ユーザー定義変換が使用されます。 それ以外の場合は、コンパイル時エラーが発生します。 最も具体的な変換は、オペランドがソース型に "最も近い" ものであり、結果の型がターゲット型に "最も近い" 変換です。 使用するユーザー定義変換を決定する場合は、最も具体的な拡大変換が使用されます。拡大変換が最も具体的でない場合は、最も具体的な縮小変換が使用されます。 最も具体的な縮小変換がない場合、変換は未定義であり、コンパイル時エラーが発生します。
次のセクションでは、最も具体的な変換がどのように決定されるかについて説明します。 次の用語を使用します。
型Aから型Bへの組み込みの拡大変換が存在し、AもBもインターフェイスでない場合、AはBとBencompasses によって包含されますA。
一連の型の 中で最も包含 型は、セット内の他のすべての型を含む 1 つの型です。 他のすべての型を含む型が 1 つもない場合、セットには最も包含される型はありません。 直感的に言うと、最も包括的な型は、セット内の "最大" 型です。つまり、他の各型を拡大変換で変換できる型です。
一連の型の 中で最も包含される 型は、セット内の他のすべての型に含まれる 1 つの型です。 他のすべての型に 1 つの型が含まれている場合、セットには最も包含される型はありません。 直感的に言えば、最も包含される型は、セット内の "最小" 型です。つまり、縮小変換を通じて他の型に変換できる型です。
型 T?のユーザー定義変換候補を収集する場合は、 T によって定義されたユーザー定義変換演算子が代わりに使用されます。 変換対象の型も null 許容値型である場合は、null 非許容値型のみを含む Tのユーザー定義変換演算子のいずれかが解除されます。
TからSへの変換演算子は、T?からS?への変換にリフトされ、必要に応じてT?からTに変換し、必要に応じてユーザー定義の変換演算子をTからSに評価し、必要に応じてSをS?に変換することによって評価されます。 ただし、変換される値がNothingの場合、リフトされた変換演算子は、S?として型指定Nothing値に直接変換します。 例えば次が挙げられます。
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
実行時に、ユーザー定義の変換を評価するには、最大 3 つの手順が必要な場合があります。
最初に、必要に応じて、値が組み込み変換を使用してソース型からオペランド型に変換されます。
次に、ユーザー定義の変換が呼び出されます。
最後に、必要に応じて、ユーザー定義変換の結果が組み込み変換を使用してターゲット型に変換されます。
ユーザー定義変換の評価には、複数のユーザー定義変換演算子が含まれることはありません。
最も具体的な拡大変換
2 つの型間で最も具体的なユーザー定義拡大変換演算子を決定するには、次の手順を使用します。
まず、すべての候補変換演算子が収集されます。 変換候補演算子は、ソース型のすべてのユーザー定義拡大変換演算子と、ターゲット型のすべてのユーザー定義拡大変換演算子です。
次に、該当しない変換演算子がすべてセットから削除されます。 変換演算子は、ソース型からオペランド型への拡張変換演算子が組み込まれており、演算子の結果からターゲット型への組み込みの拡大変換演算子がある場合に、ソース型とターゲット型に適用できます。 該当する変換演算子がない場合、最も具体的な拡大変換はありません。
次に、適用可能な変換演算子の最も具体的なソースの種類が決定されます。
変換演算子のいずれかがソース型から直接変換する場合、ソース型は最も具体的なソース型です。
それ以外の場合、最も具体的なソース型は、変換演算子のソース型の組み合わせの中で最も包含される型です。 最も包含される型が見つからない場合、最も具体的な拡大変換はありません。
次に、適用可能な変換演算子の最も具体的なターゲット型が決定されます。
変換演算子のいずれかがターゲット型に直接変換される場合、ターゲット型は最も具体的なターゲット型です。
それ以外の場合、最も具体的なターゲット型は、変換演算子のターゲット型の組み合わせの中で最も包括的な型です。 最も包含する型が見つからない場合は、最も具体的な拡大変換はありません。
次に、1 つの変換演算子が最も特定のソース型から最も特定のターゲット型に変換される場合、これが最も具体的な変換演算子になります。 このような演算子が複数存在する場合、最も具体的な拡大変換はありません。
最も具体的な縮小変換
2 つの型間で最も具体的なユーザー定義の縮小変換演算子を決定するには、次の手順を使用します。
まず、すべての候補変換演算子が収集されます。 変換候補演算子は、ソース型のすべてのユーザー定義変換演算子と、ターゲット型のすべてのユーザー定義変換演算子です。
次に、該当しない変換演算子がすべてセットから削除されます。 変換演算子は、ソース型からオペランド型への組み込み変換演算子があり、演算子の結果からターゲット型への組み込み変換演算子がある場合に、ソース型とターゲット型に適用できます。 該当する変換演算子がない場合、最も具体的な縮小変換はありません。
次に、適用可能な変換演算子の最も具体的なソースの種類が決定されます。
変換演算子のいずれかがソース型から直接変換する場合、ソース型は最も具体的なソース型です。
それ以外の場合、変換演算子のいずれかがソース型を含む型から変換される場合、最も具体的なソース型は、それらの変換演算子の結合されたソース型のセットの中で最も包含型になります。 最も包含される型が見つからない場合は、最も具体的な縮小変換はありません。
それ以外の場合、最も具体的なソース型は、変換演算子のソース型の組み合わせの中で最も包括的な型です。 最も包含する型が見つからない場合は、最も具体的な縮小変換はありません。
次に、適用可能な変換演算子の最も具体的なターゲット型が決定されます。
変換演算子のいずれかがターゲット型に直接変換される場合、ターゲット型は最も具体的なターゲット型です。
それ以外の場合、変換演算子のいずれかがターゲット型に含まれる型に変換される場合、最も具体的なターゲット型は、それらの変換演算子の結合されたソース型のセットの中で最も包含型になります。 最も包含する型が見つからない場合は、最も具体的な縮小変換はありません。
それ以外の場合、最も具体的なターゲット型は、変換演算子のターゲット型の組み合わせの中で最も包含される型です。 最も包含される型が見つからない場合は、最も具体的な縮小変換はありません。
次に、1 つの変換演算子が最も特定のソース型から最も特定のターゲット型に変換される場合、これが最も具体的な変換演算子になります。 このような演算子が複数存在する場合、最も具体的な縮小変換はありません。
ネイティブ変換
一部の変換は、.NET Framework でネイティブにサポートされているため、ネイティブ 変換 として分類されます。 これらの変換は、 DirectCast および TryCast 変換演算子、およびその他の特殊な動作を使用して最適化できる変換です。 ネイティブ変換として分類される変換は、ID 変換、既定の変換、参照変換、配列変換、値型変換、型パラメーター変換です。
ドミナント型
型のセットを指定すると、多くの場合、セットの 主要な 型を決定するために型の推論などの状況で必要になります。 型のセットの主要な型は、最初に 1 つ以上の他の型が暗黙的な変換を持たない型を削除することによって決定されます。 この時点で型が残っていない場合、主要な型はありません。 その後、主要な型は、残りの型の中で最も包含されます。 最も包含されている型が複数ある場合、主な型はありません。
Visual Basic language spec