10 変換
10.1 全般
変換では、式が特定の型に変換されたり、特定の型として扱われたりします。前者の場合、変換には表現の変更が伴う場合があります。 変換は implicit または explicit にできます。これにより、明示的なキャストが必要かどうかが決まります。
例: たとえば、型
int
から型long
への変換は暗黙的であるため、型int
の式は暗黙的に型long
として扱うことができます。 型long
から型int
への逆の変換は明示的であるため、明示的なキャストが必要です。int a = 123; long b = a; // implicit conversion from int to long int c = (int) b; // explicit conversion from long to int
end の例
一部の変換は言語によって定義されます。 プログラムは、独自の変換を定義することもできます (§10.5)。
言語の一部の変換は、式から型に定義され、他の変換は型から型に定義されます。 型からの変換は、その型を持つすべての式に適用されます。
例:
enum Color { Red, Blue, Green } // The expression 0 converts implicitly to enum types Color c0 = 0; // Other int expressions need explicit conversion Color c1 = (Color)1; // Conversion from null expression (no type) to string string x = null; // Conversion from lambda expression to delegate type Func<int, int> square = x => x * x;
end の例
10.2 暗黙的な変換
10.2.1 全般
次の変換は暗黙的な変換として分類されます。
- ID 変換 (§10.2.2)
- 暗黙的な数値変換 (§10.2.3)
- 暗黙的な列挙変換 (§10.2.4)
- 暗黙的な挿入文字列変換 (§10.2.5)
- 暗黙的な参照変換 (§10.2.8)
- ボックス変換 (§10.2.9)
- 暗黙的な動的変換 (§10.2.10)
- 暗黙的な型パラメーター変換 (§10.2.12)
- 暗黙的な定数式の変換 (§10.2.11)
- ユーザー定義 (リフトドを含む) 暗黙的な変換 (§10.2.14)
- 匿名関数の変換 (§10.2.15)
- メソッド グループ変換 (§10.2.15)
- null リテラル変換 (§10.2.7)
- 暗黙的な null 許容変換 (§10.2.6)
- 暗黙的なタプル変換 (§10.2.13)
- 既定のリテラル変換 (§10.2.16)
- 暗黙的なスロー変換 (§10.2.17)
暗黙的な変換は、関数メンバー呼び出し (§12.6.6)、キャスト式 (§12.9.7)、割り当て (§12.21) など、さまざまな状況で発生する可能性があります。
事前に定義された暗黙的な変換は常に成功し、例外がスローされることはありません。
注: 適切に設計されたユーザー定義の暗黙的な変換も、これらの特性を示す必要があります。 end note
変換のために、 object
型と dynamic
型は ID 変換可能です (§10.2.2)。
ただし、動的変換 (§10.2.10) は、 dynamic
型 (§8.2.4) の式にのみ適用されます。
10.2.2 ID 変換
ID 変換では、任意の型から同じ型または実行時に同等の型に変換されます。 この変換が存在する理由の 1 つは、型 T
または T
型の式が T
自体に変換可能であると言える点です。 次の ID 変換が存在します。
- 任意の型
T
のT
とT
の間。 - 参照型
T
のT?
とT
の間。 object
とdynamic
の間。- 同じアリティを持つすべてのタプル型と、対応する要素型の各ペア間に ID 変換が存在する場合に、対応する構築された
ValueTuple<...>
型の間。 - 対応する各型引数の間に ID 変換が存在する同じジェネリック型から構築された型間。
例: 3 番目の規則の再帰的な性質を次に示します。
(int a , string b) t1 = (1, "two"); (int c, string d) t2 = (3, "four"); // Identity conversions exist between // the types of t1, t2, and t3. var t3 = (5, "six"); t3 = t2; t2 = t1; var t4 = (t1, 7); var t5 = (t2, 8); // Identity conversions exist between // the types of t4, t5, and t6. var t6 =((8, "eight"), 9); t6 = t5; t5 = t4;
タプルの型
t1
、t2
、t3
には、いずれも 2 つの要素があります。int
の後にstring
が続きます。 タプル要素の型は、t4
、t5
、およびt6
のように、タプルによってそれ自体が使用できます。 入れ子になったタプルを含む、対応する要素型の各ペア間に ID 変換が存在するため、タプルの型t4
、t5
、およびt6
の間に ID 変換が存在します。end の例
すべての ID 変換は対称です。 T₁
からT₂
への ID 変換が存在する場合は、T₂
からT₁
への ID 変換が存在します。 2 つの型間 ID 変換が存在する場合 2 つの型が変換可能です。
ほとんどの場合、ID 変換は実行時には影響しません。 ただし、浮動小数点演算は型 (§8.3.7) で規定されているよりも高い精度で実行される可能性があるため、結果を代入すると精度が失われる可能性があり、明示的なキャストによって、型 (§12.9.7 で規定されているものに精度が低下することが保証されます。
10.2.3 暗黙的な数値変換
暗黙的な数値変換は次のとおりです。
sbyte
からshort
、int
、long
、float
、double
、またはdecimal
まで。byte
からshort
、ushort
、int
、uint
、long
、ulong
、float
、double
、またはdecimal
まで。short
からint
、long
、float
、double
、またはdecimal
まで。ushort
からint
、uint
、long
、ulong
、float
、double
、またはdecimal
まで。int
からlong
、float
、double
、またはdecimal
まで。uint
からlong
、ulong
、float
、double
、またはdecimal
まで。long
からfloat
、double
、またはdecimal
まで。ulong
からfloat
、double
、またはdecimal
まで。char
からushort
、int
、uint
、long
、ulong
、float
、double
、またはdecimal
まで。float
からdouble
。
int
、uint
、long
、またはulong
からfloat
への変換、およびlong
またはulong
からdouble
への変換は、精度の損失を引き起こす可能性がありますが、大きさが低下することはありません。 その他の暗黙的な数値変換では、情報が失われることはありません。
char
型への定義済みの暗黙的な変換がないため、他の整数型の値は自動的にchar
型に変換されません。
10.2.4 暗黙的な列挙変換
暗黙的な列挙変換を使用すると、任意の整数型と値 0 を持つconstant_expression (§12.23) を任意のenum_typeと、基になる型がenum_typeである任意のnullable_value_typeに変換できます。 後者の場合、変換は基になる enum_type に変換し、結果をラップすることによって評価されます (§8.3.12)。
10.2.5 暗黙的な挿入文字列変換
暗黙的な挿入文字列変換を使用すると、 interpolated_string_expression (§12.8.3) を System.IFormattable
または System.FormattableString
( System.IFormattable
を実装) に変換できます。
この変換を適用すると、文字列値は補間文字列から構成されません。 代わりに、§12.8.3 で詳しく説明されているように、System.FormattableString
のインスタンスが作成。
10.2.6 暗黙的な null 許容変換
暗黙的な null 許容変換は、暗黙的な定義済み変換から派生した null 許容変換 (§10.6.1) です。
10.2.7 Null リテラル変換
null
リテラルから任意の参照型または null 許容値型への暗黙的な変換が存在します。 この変換では、ターゲット型が参照型の場合、または指定された null 許容値型の null 値 (§8.3.12) の場合、null 参照が生成されます。
10.2.8 暗黙的な参照変換
暗黙的な参照変換は次のとおりです。
- 任意の reference_type から
object
とdynamic
まで。 - が
S
から派生している場合、任意のclass_typeT
から任意のS
T
に渡されます。 - 任意のclass_type
S
から任意のinterface_typeT
まで、S
を実装T
提供されます。 - が
S
から派生している場合は、任意のinterface_typeT
から任意のS
T
に渡されます。 - 要素型がされた
S
Sᵢ
から、要素型を持つT
Tᵢ
まで、次のすべてが当てはまる場合。S
とT
は要素の種類でのみ異なります。 つまり、S
とT
の次元数は同じです。Sᵢ
からTᵢ
への暗黙的な参照変換が存在します。
S[]
からSystem.Collections.Generic.IList<T>
への暗黙的な ID または参照変換がある場合、1 次元配列型からSystem.Collections.Generic.IReadOnlyList<T>
、S
、および基底インターフェイスにT
。- array_typeから
System.Array
、および実装するインターフェイスまで。 - delegate_typeから
System.Delegate
、および実装するインターフェイスまで。 - null リテラル (§6.4.5.7) から任意の参照型へ。
- 暗黙的な ID またはreference_typeへの参照変換があり、
T
が への ID 変換を持っている場合は、T₀
からT₀
T
へ。 - インターフェイスまたはデリゲート型への暗黙的な ID 変換または参照変換がある場合、インターフェイスまたはデリゲート型
T
へのT₀
から、T₀
が分散変換 (§18.2.3.3) からT
。 - 参照型と呼ばれる型パラメーターを含む暗黙的な変換。 型パラメーターを含む暗黙的な変換の詳細については、 §10.2.12 を参照してください。
暗黙的な参照変換は、常に成功することが証明できる reference_type間の変換であるため、実行時にチェックを必要としません。
参照変換 (暗黙的または明示的) では、変換されるオブジェクトの参照 ID は変更されません。
注: つまり、参照変換では参照の型を変更できますが、参照されるオブジェクトの型や値は変更されません。 end note
10.2.9 ボクシングの変換
ボックス化変換を使用すると、 value_type を暗黙的に reference_typeに変換できます。 次のボックス化変換が存在します。
- 任意の value_type から型
object
。 - 任意の value_type から型
System.ValueType
。 - 任意の enum_type から型
System.Enum
。 - 任意のnon_nullable_value_typeから、non_nullable_value_typeによって実装された任意のinterface_typeまで。
- non_nullable_value_typeから別のinterface_type
I
へのボックス化変換があり、はへの ID 変換を持つ任意のI₀
から任意のI₀
I
に変換されます。 - non_nullable_value_typeから別のinterface_type
I
へのボックス化変換が行われ、が分散変換 (§18.2.3.3) からI₀
に変換されるように、任意のI₀
から任意のinterface_typeI
に変換されます。 - nullable_value_typeの基になる型からreference_typeへのボックス化変換がある任意のnullable_value_typeから任意のreference_type。
- §10.2.12 で変換が許可されるように、どの型への参照型とも見なされない型パラメーターから。
non-nullable-value-type の値をボックス化は、オブジェクト インスタンスを割り当て、そのインスタンスに値をコピーすることで構成されます。
nullable_value_typeの値をボックス化すると、null 値 (HasValue
が false) の場合は null 参照が生成されます。それ以外の場合は、基になる値をラップ解除してボックス化した結果が生成されます。
注: ボックス化のプロセスは、すべての値型に対してボックス化クラスが存在するという点で想像される場合があります。 たとえば、
struct S
と呼ばれるボックス化クラスを使用して、インターフェイスI
を実装するS_Boxing
について考えます。interface I { void M(); } struct S : I { public void M() { ... } } sealed class S_Boxing : I { S value; public S_Boxing(S value) { this.value = value; } public void M() { value.M(); } }
v
型の値S
をボックス化することは、式new S_Boxing(v)
を実行し、変換のターゲット型の値として結果のインスタンスを返す処理で構成されるようになりました。 したがって、ステートメントS s = new S(); object box = s;
は、次のように考えることができます。
S s = new S(); object box = new S_Boxing(s);
上で説明した想像されるボックス化の種類は、実際には存在しません。 代わりに、
S
型のボックス化された値にはランタイム型S
があり、右オペランドとして値型を持つis
演算子を使用するランタイム型チェックでは、左オペランドが右オペランドのボックス化されたバージョンであるかどうかをテストします。 たとえば、 にします。int i = 123; object box = i; if (box is int) { Console.Write("Box contains an int"); }
は次のように出力します。
Box contains an int
ボックス化変換は、ボックス化されている値のコピーを作成することを意味します。 これは、 reference_type から型
object
への変換とは異なり、値は引き続き同じインスタンスを参照し続け、単にobject
派生型とは見なされます。 たとえば、次の例を示します。struct Point { public int x, y; public Point(int x, int y) { this.x = x; this.y = y; } } class A { void M() { Point p = new Point(10, 10); object box = p; p.x = 20; Console.Write(((Point)box).x); } }
では、
p
をbox
に割り当て中に発生する暗黙的なボックス化操作によってp
の値がコピーされるため、コンソールに値 10 が出力されます。Point
代わりにclass
として宣言されていた場合、p
とbox
が同じインスタンスを参照するため、値 20 が出力されます。ボックス化クラスの類似点は、ボックス化の概念的な動作を示す便利なツールとして使用しないでください。 この仕様で説明されている動作と、この方法で正確に実装されたボックス化の結果として生じる動作には、多くの微妙な違いがあります。
end note
10.2.10 暗黙的な動的変換
動的型の式から任意の型 T
への暗黙的な動的変換が存在します。 変換は動的にバインド §12.3.3。つまり、式の実行時の型から T
への暗黙的な変換が実行時に検索されます。 変換が見つからない場合は、実行時例外がスローされます。
この暗黙的な変換は、 §10.2 の最初のアドバイスに違反しているように見えます。暗黙的な変換によって例外が発生することはありません。 ただし、変換自体ではなく、例外の原因となる変換の finding です。 ランタイム例外のリスクは、動的バインディングの使用に固有です。 変換の動的バインディングが必要でない場合は、式を最初に object
に変換してから、目的の型に変換できます。
例: 暗黙的な動的変換を次に示します。
object o = "object"; dynamic d = "dynamic"; string s1 = o; // Fails at compile-time – no conversion exists string s2 = d; // Compiles and succeeds at run-time int i = d; // Compiles but fails at run-time – no conversion exists
s2
とi
への割り当てはどちらも暗黙的な動的変換を使用します。この場合、操作のバインドは実行時まで中断されます。 実行時に、d
(string
) の実行時の型からターゲット型への暗黙的な変換が求められます。string
への変換は検出されますが、int
には見つかりません。end の例
10.2.11 暗黙的な定数式の変換
暗黙的な定数式の変換では、次の変換が可能です。
- 型のconstant_expression (
int
) は、sbyte
の値が変換先の型の範囲内であれば、byte
、short
、ushort
、uint
、ulong
、またはに変換できます。 - constant_expressionの値が負でない場合、
long
型のulong
を型に変換できます。
10.2.12 型パラメーターを含む暗黙的な変換
参照型 (§15.2.5) であることがわかっているT
には、次の暗黙的な参照変換 (§10.2.8) が存在します。
T
から有効な基底クラスC
、T
からC
の任意の基底クラスまで、T
からC
によって実装された任意のインターフェイスまで。T
から、の効果的なインターフェイス セット内のI
T
、T
からI
の任意の基本インターフェイスまで。T
から型パラメーターへのU
T
がU
に依存している場合 (§15.2.5)。注:
T
は参照型であることが知られているため、T
のスコープ内では、U
がコンパイル時に参照型であることが判明していない場合でも、U
の実行時の型は常に参照型になります。 end note- null リテラル (§6.4.5.7) から T へ。
参照型§15.2.5T
と認識されていないtype_parameterの場合、T
に関連する次の変換は、コンパイル時にボックス化変換 (§10.2.9) と見なされます。 実行時に、 T
が値型の場合、変換はボックス化変換として実行されます。 実行時に、 T
が参照型の場合、変換は暗黙的な参照変換または ID 変換として実行されます。
T
から有効な基底クラスC
、T
からC
の任意の基底クラスまで、T
からC
によって実装された任意のインターフェイスまで。注:
C
は、System.Object
、System.ValueType
、またはSystem.Enum
のいずれかの型になります (それ以外の場合は、T
参照型と呼ばれます)。 end noteT
から、の効果的なインターフェイス セット内のI
T
、T
からI
の任意の基本インターフェイスまで。
参照型知られていないT
の場合、T
から型パラメーターへの暗黙的な変換U
、T
に依存U
提供されます。 実行時に、 T
が値型で、 U
が参照型の場合、変換はボックス化変換として実行されます。 実行時に、 T
と U
の両方が値型である場合、 T
と U
は必ずしも同じ型であり、変換は実行されません。 実行時に、 T
が参照型の場合、 U
は必ずしも参照型でもあり、変換は暗黙的な参照変換または ID 変換 (§15.2.5) として実行されます。
特定の型パラメーター T
に対して、さらに次の暗黙的な変換が存在します。
T
から参照型へのS
、参照型S₀
への暗黙的な変換があり、S₀
がS
への ID 変換を持っている場合。 実行時には、S₀
への変換と同じ方法で変換が実行されます。T
からインターフェイス型へのI
インターフェイス型への暗黙的な変換があり、I₀
がI₀
I
に分散変換可能である場合 (§18.2.3.3)。 実行時に、T
が値型の場合、変換はボックス化変換として実行されます。 それ以外の場合、変換は暗黙的な参照変換または ID 変換として実行されます。
いずれの場合も、実行時に変換が値型から参照型に変換される場合にのみ、変換がボックス化変換として実行されることがルールによって保証されます。
10.2.13 暗黙的なタプル変換
E
がT
と同じアリティを持ち、E
内の各要素からT
内の対応する要素 E
型への暗黙的な変換が存在する場合、タプル式T
からタプル型への暗黙的な変換が存在します。 変換を実行するには、 T
の対応する System.ValueTuple<...>
型のインスタンスを作成し、その各フィールドを左から右に順に初期化します。そのためには、 E
の対応するタプル要素式を評価し、見つかった暗黙的な変換を使用して対応する T
の要素型に変換し、結果でフィールドを初期化します。
タプル式の要素名がタプル型の対応する要素名と一致しない場合は、警告が発行されます。
例:
(int, string) t1 = (1, "One"); (byte, string) t2 = (2, null); (int, string) t3 = (null, null); // Error: No conversion (int i, string s) t4 = (i: 4, "Four"); (int i, string) t5 = (x: 5, s: "Five"); // Warning: Names are ignored
要素式から対応する要素型への暗黙的な変換が存在するため、
t1
、t2
、t4
、およびt5
の宣言はすべて有効です。t3
からnull
への変換がないため、int
の宣言は無効です。 タプル式の要素名がタプル型の要素名と異なるため、t5
の宣言によって警告が発生します。end の例
10.2.14 ユーザー定義の暗黙的な変換
ユーザー定義の暗黙的な変換は、オプションの標準の暗黙的な変換で構成され、その後にユーザー定義の暗黙的な変換演算子が実行され、その後に別のオプションの標準暗黙的な変換が続きます。 ユーザー定義の暗黙的な変換を評価するための正確な規則については、 §10.5.4で説明されています。
10.2.15 匿名関数の変換とメソッド グループ変換
匿名関数とメソッド グループ自体には型がありませんが、デリゲート型に暗黙的に変換される場合があります。 さらに、一部のラムダ式は、式ツリー型に暗黙的に変換される場合があります。 匿名関数の変換の詳細については、 §10.7 および §10.8 のメソッド グループ変換で詳しく説明します。
10.2.16 既定のリテラル変換
default_literal (§12.8.21) から任意の型への暗黙的な変換が存在します。 この変換により、推論された型の既定値 (§9.3) が生成されます。
10.2.17 暗黙的なスロー変換
throw 式には型はありませんが、暗黙的に任意の型に変換できます。
10.3 明示的な変換
10.3.1 全般
次の変換は、明示的な変換として分類されます。
- すべての暗黙的な変換 (§10.2)
- 明示的な数値変換 (§10.3.2)
- 明示的な列挙変換 (§10.3.3)
- 明示的な null 許容変換 (§10.3.4)
- 明示的なタプル変換 (§10.3.6)
- 明示的な参照変換 (§10.3.5)
- 明示的なインターフェイス変換
- 変換のボックス化解除 (§10.3.7)
- 明示的な型パラメーター変換 (§10.3.8)
- ユーザー定義の明示的な変換 (§10.3.9)
明示的な変換は、キャスト式で行うことができます (§12.9.7)。
明示的な変換のセットには、すべての暗黙的な変換が含まれます。
注: たとえば、特定のメソッド オーバーロードを強制的に選択するために、暗黙的な ID 変換が存在する場合に明示的なキャストを使用できます。 end note
暗黙的な変換ではない明示的な変換は、常に成功することが証明できない変換、情報が失われる可能性が知られている変換、および型のドメイン間の変換は、明示的な表記法に見合うのに十分に異なります。
10.3.2 明示的な数値変換
明示的な数値変換とは、暗黙的な数値変換 (§10.2.3) がまだ存在しないnumeric_typeから別のnumeric_typeへの変換です。
sbyte
からbyte
、ushort
、uint
、ulong
、またはchar
まで。byte
からsbyte
またはchar
まで。short
からsbyte
、byte
、ushort
、uint
、ulong
、またはchar
まで。ushort
からsbyte
、byte
、short
、または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
まで。
明示的な変換にはすべての暗黙的な数値変換と明示的な数値変換が含まれているため、キャスト式 (§12.9.7) を使用して、任意のnumeric_typeから他のnumeric_typeに常に変換できます。
明示的な数値変換では、情報が失われるか、例外がスローされる可能性があります。 明示的な数値変換は、次のように処理されます。
- 整数型から別の整数型への変換の場合、処理は、変換が行われるオーバーフロー チェック コンテキスト (§12.8.20) に依存します。
checked
コンテキストでは、ソース オペランドの値が変換先の型の範囲内にある場合は変換は成功しますが、ソース オペランドの値が変換先の型の範囲外の場合はSystem.OverflowException
をスローします。unchecked
コンテキストでは、変換は常に成功し、次のように進みます。- ソースの種類が変換先の型よりも大きい場合は、その "余分な" 最上位ビットを破棄することで、ソース値が切り捨てられます。 結果は変換先の型の値として扱われます。
- ソースの種類がコピー先の型と同じサイズの場合、ソース値は変換先の型の値として扱われます
decimal
から整数型への変換の場合、ソース値は 0 から最も近い整数値に丸められ、この整数値は変換の結果になります。 結果の整数値が変換先の型の範囲外の場合は、System.OverflowException
がスローされます。float
またはdouble
から整数型への変換の場合、処理は、変換が行われるオーバーフロー チェック コンテキスト (§12.8.20) に依存します。- チェックされたコンテキストでは、変換は次のように進みます。
- オペランドの値が NaN または無限の場合は、
System.OverflowException
がスローされます。 - それ以外の場合、ソース オペランドは 0 に向かって最も近い整数値に丸められます。 この整数値が変換先の型の範囲内にある場合、この値は変換の結果になります。
- それ以外の場合は、
System.OverflowException
がスローされます。
- オペランドの値が NaN または無限の場合は、
- チェックされていないコンテキストでは、変換は常に成功し、次のように進みます。
- オペランドの値が NaN または無限の場合、変換の結果は変換先の型の指定されていない値になります。
- それ以外の場合、ソース オペランドは 0 に向かって最も近い整数値に丸められます。 この整数値が変換先の型の範囲内にある場合、この値は変換の結果になります。
- それ以外の場合、変換の結果は変換先の型の未指定の値になります。
- チェックされたコンテキストでは、変換は次のように進みます。
double
からfloat
への変換では、double
値は最も近いfloat
値に丸められます。double
値が小さすぎてfloat
として表すと、結果は値と同じ符号を持つゼロになります。double
値の大きさが大きすぎてfloat
として表すと、結果は値と同じ符号で無限大になります。double
値が NaN の場合、結果も NaN になります。float
またはdouble
からdecimal
への変換の場合、ソース値はdecimal
表現に変換され、必要に応じて最も近い数値に丸められます (§8.3.8)。- ソース値が小さすぎて
decimal
として表すには、結果は 0 になり、decimal
が符号付きゼロ値をサポートしている場合、元の値の符号は保持されます。 - ソース値の大きさが大きすぎて
decimal
として表現できない場合、またはその値が無限大である場合、10 進表現で無限大がサポートされている場合、結果は元の値の符号を保持する無限大になります。それ以外の場合は、System.OverflowException がスローされます。 - ソース値が NaN の場合、10 進表現で NaN がサポートされている場合、結果は NaN になります。それ以外の場合は、System.OverflowException がスローされます。
- ソース値が小さすぎて
decimal
からfloat
またはdouble
への変換では、decimal
値は最も近いdouble
またはfloat
値に丸められます。 ソース値の大きさが大きすぎてターゲット型で表現できない場合、またはその値が無限大の場合、結果は元の値の符号を保持する無限大になります。 ソース値が NaN の場合、結果は NaN になります。 この変換では精度が失われる可能性があります。ただし、例外がスローされることはありません。
注:
decimal
型は無限大または NaN 値をサポートするために必要ありませんが、その場合もあります。その範囲は、float
とdouble
の範囲よりも小さい場合がありますが、保証されません。 無限大または NaN 値のないdecimal
表現で、範囲がfloat
より小さい場合、decimal
からfloat
またはdouble
への変換の結果は無限大または NaN になることはありません。 end note
10.3.3 明示的な列挙変換
明示的な列挙変換は次のとおりです。
sbyte
、byte
、short
、ushort
、int
、uint
、long
、ulong
、char
、float
、double
、またはdecimal
から任意のenum_typeまで。- 任意の enum_type から、
sbyte
、byte
、short
、ushort
、int
、uint
、long
、ulong
、char
、float
、double
、またはdecimal
まで。 - 任意の enum_type から他の enum_typeまで。
2 つの型間の明示的な列挙変換は、参加している enum_type をその enum_typeの基になる型として扱い、結果の型間で暗黙的または明示的な数値変換を実行することによって処理されます。
例: の型と基になる型を持つ
E
int
を指定すると、E
からbyte
への変換は、からint
への明示的な数値変換 (byte
) として処理され、byte
からE
への変換は、 からbyte
への暗黙的な数値変換 (int
) として処理されます。 end の例
10.3.4 明示的な null 許容変換
明示的な null 許容変換は、明示的および暗黙的な定義済み変換から派生した null 許容変換 (§10.6.1) です。
10.3.5 明示的な参照変換
明示的な参照変換は次のとおりです。
- オブジェクトから他の reference_typeへ。
- class_type
S
から任意のclass_typeT
まで、S
はT
の基底クラスです。 - class_type
S
から任意のinterface_typeT
まで、S
がシールされておらず、S
を実装していないT
指定されている場合。 - interface_type
S
から任意のclass_typeT
まで、T
がシールされていないか、T
を実装S
提供されない場合。 - が
S
から派生していない場合、任意のinterface_typeT
から任意のS
T
に渡されます。 - 要素型がされた
S
Sᵢ
から、要素型を持つT
Tᵢ
まで、次のすべてが当てはまる場合。S
とT
は要素の種類でのみ異なります。 つまり、S
とT
の次元数は同じです。- 明示的な参照変換は、
Sᵢ
からTᵢ
に存在します。
System.Array
と実装するインターフェイスから、任意のarray_typeに。- から
S[]
への ID 変換または明示的な参照変換がある場合は、1 次元のSystem.Collections.Generic.IList<T>
System.Collections.Generic.IReadOnlyList<T>
からS
、T
、およびその基本インターフェイスに変換します。 System.Collections.Generic.IList<S>
から T への ID 変換または明示的な参照変換がある場合、System.Collections.Generic.IReadOnlyList<S>
、T[]
、およびその基本インターフェイスから 1 次元配列型のS
。System.Delegate
とそれが実装するインターフェイスから任意のdelegate_typeに。- 参照型
S
から参照型へのT
S
から参照型への明示的な変換があり、T₀
およびT₀
があり、T₀
からT
への ID 変換がある場合。 S
からインターフェイスまたはデリゲート型への明示的な参照変換があり、T
がS
に分散変換可能であるか、T₀
がT₀
T
に分散変換可能である場合、参照型T
からインターフェイスまたはデリゲートT₀
型への。D<S₁...Sᵥ>
から、D<T₁...Tᵥ>
がジェネリック デリゲート型であるD<X₁...Xᵥ>
まで、D<S₁...Sᵥ>
はD<T₁...Tᵥ>
と互換性も同じでなく、Xᵢ
の型パラメーターD
ごとに次の内容が保持されます。Xᵢ
が不変の場合、Sᵢ
はTᵢ
と同じです。Xᵢ
共変の場合、ID 変換、暗黙的な参照変換、またはSᵢ
からTᵢ
への明示的な参照変換があります。Xᵢ
が反変の場合、Sᵢ
とTᵢ
は同一または両方の参照型になります。
- 参照型と呼ばれる型パラメーターを含む明示的な変換。 型パラメーターに関連する明示的な変換の詳細については、 §10.3.8 を参照してください。
明示的な参照変換は、 reference_type間の変換であり、それらが正しいことを確認するために実行時チェックが必要です。
実行時に明示的な参照変換が成功するには、ソース オペランドの値を null
するか、ソース オペランドによって参照されるオブジェクトの型を暗黙的な参照変換 (§10.2.8 によって変換先の型に変換できる型にする必要があります。 明示的な参照変換が失敗すると、 System.InvalidCastException
がスローされます。
注: 参照変換は暗黙的または明示的であり、参照自体の値 (§8.2.1) は変更されません。参照されるオブジェクトの型または値は変更されません。 end note
10.3.6 明示的なタプル変換
E
がT
と同じアリティを持ち、E
内の各要素からT
内の対応する要素型への暗黙的または明示的な変換が存在する場合は、タプル式E
からタプル型T
への明示的な変換が存在します。 変換は、 T
の対応する System.ValueTuple<...>
型のインスタンスを作成し、 E
の対応するタプル要素式を評価し、見つかった明示的な変換を使用して対応する T
の要素型に変換し、結果を含むフィールドを初期化することによって、各フィールドを左から右に順に初期化することによって実行されます。
10.3.7 変換のボックス化解除
ボックス化解除変換を使用すると、 reference_type を明示的に value_typeに変換できます。 次のボックス化解除変換が存在します。
- 型から任意の
object
に。 - 型から任意の
System.ValueType
に。 - 型から任意の
System.Enum
に。 - 任意のinterface_typeから、interface_typeを実装する任意のnon_nullable_value_typeまで。
- 任意のinterface_type
I
から、interface_typeからI₀
からI
への ID 変換がある任意のI₀
。 - 任意のinterface_type
I
から、interface_typeからI₀
non_nullable_value_typeへのボックス化解除変換があり、I₀
がI
にvariance_convertibleされているか、I
がI₀
に分散変換可能である (§18.2.3.3)。 - reference_typeから、reference_typeからnullable_value_typeの基になるnon_nullable_value_typeへのボックス化解除変換がある任意のnullable_value_type。
- §10.3.8 で変換が許可されるように、値型として認識されていない型パラメーターから任意の型に。
non_nullable_value_typeに対するボックス化解除操作は、最初にオブジェクト インスタンスが指定されたnon_nullable_value_typeのボックス化された値であることを確認してから、インスタンスから値をコピーすることで構成されます。
nullable_value_typeへのボックス化を解除すると、ソース オペランドがされている場合はnull
の null 値が生成され、それ以外の場合はオブジェクト インスタンスのボックス化を解除した結果nullable_value_typeそれ以外の場合は生成されます。
注: §10.2.9 で説明されている架空のボックス化クラスを参照すると、オブジェクト ボックスから value_type
S
へのボックス化解除変換は、式((S_Boxing)box).value
の実行で構成されます。 したがって、ステートメントobject box = new S(); S s = (S)box;
概念的には次に対応します。
object box = new S_Boxing(new S()); S s = ((S_Boxing)box).value;
end note
特定の non_nullable_value_type へのボックス化解除変換が実行時に成功するには、ソース オペランドの値は、その non_nullable_value_typeのボックス化された値への参照である必要があります。 ソース オペランドが null
場合は、 System.NullReferenceException
がスローされます。 ソース オペランドが互換性のないオブジェクトへの参照である場合は、 System.InvalidCastException
がスローされます。
特定のnullable_value_typeへのボックス化解除変換が実行時に成功するには、ソース オペランドの値が null であるか、nullable_value_typeの基になるnon_nullable_value_typeのボックス化された値への参照である必要があります。 ソース オペランドが互換性のないオブジェクトへの参照である場合は、 System.InvalidCastException
がスローされます。
10.3.8 型パラメーターを含む明示的な変換
参照型 (§15.2.5) であることがわかっているT
には、次の明示的な参照変換 (§10.3.5) が存在します。
C
の有効な基底クラスT
からT
まで、C
の任意の基底クラスからT
まで。- 任意の interface_type から
T
。 T
から任意のinterface_typeI
へのT
からI
への暗黙的な参照変換がまだ存在しない場合。- が
U
(T
) に依存している場合、T
U
から。注:
T
は参照型であることが知られているため、T
のスコープ内では、コンパイル時にU
が参照型であることが判明していない場合でも、実行時の型は常に参照型になります。 end note
参照型 (T
) と呼ばれるtype_parameterの場合、T
に関連する次の変換は、コンパイル時に変換のボックス化解除 (§10.3.7) と見なされます。 実行時に、 T
が値型の場合、変換はボックス化解除変換として実行されます。 実行時に、 T
が参照型の場合、変換は明示的な参照変換または ID 変換として実行されます。
C
の有効な基底クラスT
からT
まで、C
の任意の基底クラスからT
まで。注: C は、
System.Object
、System.ValueType
、またはSystem.Enum
のいずれかの型になります (それ以外の場合は、T
参照型と呼ばれます)。 end note- 任意の interface_type から
T
。
参照型 (§15.2.5) であることがT
知られていないtype_parameterの場合、次の明示的な変換が存在します。
T
からへの暗黙的な変換がまだ存在しない場合は、I
から任意のT
I
に変換します。 この変換は、からT
への暗黙的なボックス化変換 (object
) の後に、object
からI
への明示的な参照変換で構成されます。 実行時に、T
が値型の場合、変換はボックス化変換として実行され、その後に明示的な参照変換が実行されます。 実行時に、T
が参照型の場合、変換は明示的な参照変換として実行されます。U
がT
(T
) に依存している場合は、型パラメーターU
から。 実行時に、T
が値型で、U
が参照型の場合、変換はボックス化解除変換として実行されます。 実行時に、T
とU
の両方が値型である場合、T
とU
は必ずしも同じ型であり、変換は実行されません。 実行時に、T
が参照型の場合、U
は必ずしも参照型でもあり、変換は明示的な参照変換または ID 変換として実行されます。
いずれの場合も、実行時に変換が参照型から値型に変換される場合にのみ、変換がボックス化解除変換として実行されます。
上記の規則では、制約のない型パラメーターから非インターフェイス型への直接の明示的な変換は許可されていません。これは驚くべき可能性があります。 このルールの理由は、混乱を防ぎ、そのような変換のセマンティクスを明確にするためです。
例: 次の宣言を検討してください。
class X<T> { public static long F(T t) { return (long)t; // Error } }
t
からlong
への直接の明示的な変換が許可されている場合、X<int>.F(7)
は7L
を返すと簡単に予想できます。 ただし、標準の数値変換は、バインド時に型が数値であることがわかっている場合にのみ考慮されるため、考慮されません。 セマンティクスを明確にするには、上記の例を代わりに記述する必要があります。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
を実行すると実行時に例外がスローされます。end の例
10.3.9 ユーザー定義の明示的な変換
ユーザー定義の明示的な変換は、オプションの標準の明示的な変換で構成され、その後にユーザー定義の暗黙的または明示的な変換演算子が実行され、その後に別のオプションの標準明示的変換が実行されます。 ユーザー定義の明示的な変換を評価するための正確な規則については、 §10.5.5 を参照してください。
10.4 標準変換
10.4.1 全般
標準変換は、ユーザー定義変換の一部として発生する可能性がある事前定義済みの変換です。
10.4.2 標準の暗黙的な変換
次の暗黙的な変換は、標準の暗黙的な変換として分類されます。
- ID 変換 (§10.2.2)
- 暗黙的な数値変換 (§10.2.3)
- 暗黙的な null 許容変換 (§10.2.6)
- null リテラル変換 (§10.2.7)
- 暗黙的な参照変換 (§10.2.8)
- ボックス変換 (§10.2.9)
- 暗黙的な定数式の変換 (§10.2.11)
- 型パラメーターを含む暗黙的な変換 (§10.2.12)
標準の暗黙的な変換では、ユーザー定義の暗黙的な変換は特に除外されます。
10.4.3 標準の明示的な変換
標準の明示的な変換は、すべて標準の暗黙的な変換に加えて、反対の標準の暗黙的な変換が存在する明示的な変換のサブセットです。
注: つまり、型
A
から型B
への標準の暗黙的な変換が存在する場合、標準の明示的な変換は型A
から型B
に、型B
から型A
に存在します。 end note
10.5 ユーザー定義の変換
10.5.1 全般
C# では、ユーザー定義の変換によって、事前に定義された暗黙的および明示的な変換を拡張できます。 ユーザー定義の変換は、クラス型と構造体型で変換演算子 (§15.10.4) を宣言することによって導入されます。
10.5.2 許可されるユーザー定義変換
C# では、特定のユーザー定義変換のみを宣言できます。 特に、既存の暗黙的または明示的な変換を再定義することはできません。
特定のソース型 S
とターゲット型 T
の場合、 S
または T
が null 許容値型の場合は、 S₀
と T₀
が基になる型を参照します。それ以外の場合、 S₀
と T₀
はそれぞれ S
と T
と等しくなります。 クラスまたは構造体は、次のすべてに該当する場合にのみ、ソース型 S
からターゲット型への変換 T
宣言できます。
S₀
とT₀
は異なる型です。S₀
またはT₀
は、演算子宣言が行われるクラスまたは構造体型です。S₀
もT₀
もinterface_type。- ユーザー定義の変換を除き、
S
からT
への変換、またはT
からS
への変換は存在しません。
ユーザー定義の変換に適用される制限は、 §15.10.4 で指定されています。
10.5.3 ユーザー定義変換の評価
ユーザー定義の変換では、source 式source 型を別の型 (ターゲット型と呼ばれる) に変換します。 ユーザー定義変換の評価は、ソース式とターゲット型の最も 固有の ユーザー定義変換演算子を見つけることを中心にしています。 この決定は、いくつかの手順に分かれています。
- ユーザー定義の変換演算子が考慮されるクラスと構造体のセットを見つける。 このセットは、ソース型とその基底クラス (ソース型が存在する場合) と、ターゲット型とその基底クラスで構成されます。 この目的のために、ユーザー定義演算子を宣言できるのはクラスと構造体のみであり、クラス以外の型には基底クラスがないことを前提としています。 また、ソースまたはターゲットの型が null 許容値型の場合は、基になる型が代わりに使用されます。
- その型のセットから、適用可能なユーザー定義およびリフトされた変換演算子を決定します。 変換演算子を適用するには、ソース式から演算子のオペランド型への標準変換 (§10.4) を実行でき、演算子の結果型からターゲット型への標準変換を実行できる必要があります。
- 適用可能なユーザー定義演算子のセットから、どの演算子が最も具体的であるかを明確に判断します。 一般に、最も具体的な演算子は、オペランド型がソース式に "最も近い" 演算子であり、結果の型がターゲット型に "最も近い" 演算子です。 ユーザー定義の変換演算子は、リフトされた変換演算子よりも優先されます。 最も具体的なユーザー定義変換演算子を確立するための正確な規則は、次のサブクラスで定義されています。
最も具体的なユーザー定義変換演算子が特定されると、ユーザー定義変換の実際の実行には、最大 3 つの手順が含まれます。
- まず、必要に応じて、ソース式からユーザー定義またはリフト変換演算子のオペランド型への標準変換を実行します。
- 次に、ユーザー定義またはリフトされた変換演算子を呼び出して変換を実行します。
- 最後に、必要に応じて、ユーザー定義変換演算子の結果型からターゲット型への標準変換を実行します。
ユーザー定義変換の評価には、複数のユーザー定義またはリフトされた変換演算子が含まれることはありません。 つまり、型 S
から型 T
への変換では、最初に S
から X
へのユーザー定義変換を実行してから、 X
から T
へのユーザー定義変換を実行することはありません。
- ユーザー定義の暗黙的または明示的な変換の評価の正確な定義は、次のサブクラスで指定します。 定義では、次の用語を使用します。
- 標準の暗黙的な変換 (§10.4.2) が型
A
から型B
に存在し、A
もB
もinterface_types
されていない場合、A
はB
によって暗号化され、B
は encompass と言われますA
。 - 式から型
E
への標準の暗黙的な変換 (B
) が存在する場合、B
もE
の種類もinterface_types
されていない場合、E
はB
によって侵入され、B
はencompassと言われますE
。 - 一連の型 最も包含する型 は、セット内の他のすべての型を含む 1 つの型です。 他のすべての型を含む型が 1 つもない場合、セットには最も包括的な型はありません。 より直感的な用語では、最も包括的な型は、セット内の "最大" 型です。これは、他の各型を暗黙的に変換できる型です。
- 一連の型 最も包含される型 は、セット内の他のすべての型に含まれる 1 つの型です。 他のすべての型に単一の型が含まれている場合、セットには最も包含される型はありません。 より直感的な用語では、最も包含される型は、セット内の "最小" 型です。これは、他の各型に暗黙的に変換できる型です。
10.5.4 ユーザー定義の暗黙的な変換
式 E
から型 T
へのユーザー定義の暗黙的な変換は、次のように処理されます。
S
、S₀
、T₀
の種類を決定します。E
に型がある場合は、その型S
します。S
またはT
が null 許容値型の場合は、Sᵢ
とTᵢ
を基になる型にし、それ以外の場合は、Sᵢ
とTᵢ
をそれぞれS
し、T
できるようにします。Sᵢ
またはTᵢ
が型パラメーターの場合は、S₀
とT₀
を有効な基底クラスにし、それ以外の場合は、S₀
とT₀
をそれぞれSₓ
し、Tᵢ
できるようにします。
ユーザー定義の変換演算子が考慮される型のセット (
D
) を検索します。 このセットは、S₀
(S₀
が存在し、クラスまたは構造体である場合)、S₀
の基底クラス (S₀
が存在し、クラスである場合)、およびT₀
(T₀
がクラスまたは構造体の場合) で構成されます。 セットに既に含まれている別の型への ID 変換が存在しない場合にのみ、型がセットD
に追加されます。適用可能なユーザー定義およびリフトされた変換演算子のセット (
U
) を検索します。 このセットは、D
を含む型からE
に含まれる型に変換するT
のクラスまたは構造体によって宣言された、ユーザー定義およびリフトされた暗黙的な変換演算子で構成されます。U
が空の場合、変換は未定義であり、コンパイル時エラーが発生します。S
が存在し、U
から変換S
演算子のいずれかが存在する場合、Sₓ
はS
。- それ以外の場合、
Sₓ
は、U
の演算子のソース型の組み合わせのセットで最も包含される型です。 最も包含されている型が 1 つだけ見つからない場合、変換はあいまいであり、コンパイル時エラーが発生します。
Tₓ
で演算子の最も具体的なターゲット型 (U
) を検索します。U
の演算子のいずれかがT
に変換された場合、Tₓ
はT
。- それ以外の場合、
Tₓ
は、U
の演算子のターゲット型の組み合わせセットで最も包括的な型です。 最も包含する型が 1 つだけ見つからない場合、変換はあいまいになり、コンパイル時エラーが発生します。
最も具体的な変換演算子を検索します。
U
Sₓ
からTₓ
に変換するユーザー定義変換演算子が 1 つだけ含まれている場合、これは最も具体的な変換演算子です。- それ以外の場合、
U
Sₓ
からTₓ
に変換するリフト変換演算子が 1 つだけ含まれている場合、これは最も具体的な変換演算子です。 - それ以外の場合、変換があいまいになり、コンパイル時エラーが発生します。
最後に、変換を適用します。
- E に型
Sₓ
がない場合は、E
からSₓ
への標準的な暗黙的な変換が実行されます。 - 最も具体的な変換演算子は、
Sₓ
からTₓ
に変換するために呼び出されます。 Tₓ
がT
されていない場合は、Tₓ
からT
への標準的な暗黙的な変換が実行されます。
- E に型
S
型から型T
へのユーザー定義の暗黙的な変換は、S
型の変数からT
へのユーザー定義の暗黙的な変換が存在する場合に存在します。
10.5.5 ユーザー定義の明示的な変換
式 E
から型 T
へのユーザー定義の明示的な変換は、次のように処理されます。
S
、S₀
、T₀
の種類を決定します。E
に型がある場合は、その型S
します。S
またはT
が null 許容値型の場合は、Sᵢ
とTᵢ
を基になる型にし、それ以外の場合は、Sᵢ
とTᵢ
をそれぞれS
し、T
できるようにします。Sᵢ
またはTᵢ
が型パラメーターの場合は、S₀
とT₀
を有効な基底クラスにし、それ以外の場合は、S₀
とT₀
をそれぞれSᵢ
し、Tᵢ
できるようにします。
- ユーザー定義の変換演算子が考慮される型のセット (
D
) を検索します。 このセットは、S₀
(S₀
が存在し、クラスまたは構造体である場合)、S₀
の基底クラス (S₀
が存在し、クラスである場合)、T₀
(T₀
がクラスまたは構造体の場合)、およびT₀
の基底クラス (T₀
がクラスの場合) で構成されます。A
type は、既にセットに含まれている別の型への ID 変換が存在しない場合にのみD
セットに追加されます。 - 適用可能なユーザー定義およびリフトされた変換演算子のセット (
U
) を検索します。 このセットは、D
を含む型またはE
(存在する場合) で囲まれる型からS
に包含される型に変換するT
のクラスまたは構造体によって宣言された、ユーザー定義およびリフトされた暗黙的または明示的な変換演算子で構成されます。U
が空の場合、変換は未定義であり、コンパイル時エラーが発生します。 Sₓ
で演算子の最も具体的なソースの種類 (U
) を見つけます。- S が存在し、
U
から変換S
演算子のいずれかが存在する場合、Sₓ
はS
。 - それ以外の場合、
U
の演算子のいずれかがE
を含む型から変換する場合、Sₓ
は、それらの演算子の結合されたソース型のセットで最も包含型になります。 最も包含される型が見つからない場合、変換はあいまいであり、コンパイル時エラーが発生します。 - それ以外の場合、
Sₓ
は、U
の演算子のソース型の組み合わせのセットで最も包括的な型です。 最も包含する型が 1 つだけ見つからない場合、変換はあいまいになり、コンパイル時エラーが発生します。
- S が存在し、
Tₓ
で演算子の最も具体的なターゲット型 (U
) を検索します。U
の演算子のいずれかがT
に変換された場合、Tₓ
はT
。- それ以外の場合、
U
の演算子のいずれかがT
に含まれる型に変換される場合、Tₓ
は、それらの演算子のターゲット型の組み合わせの中で最も包括的な型になります。 最も包含する型が 1 つだけ見つからない場合、変換はあいまいになり、コンパイル時エラーが発生します。 - それ以外の場合、
Tₓ
は、U
の演算子のターゲット型の組み合わせのセットで最も包含される型です。 最も包含される型が見つからない場合、変換はあいまいであり、コンパイル時エラーが発生します。
- 最も具体的な変換演算子を検索します。
- U に、
Sₓ
からTₓ
に変換するユーザー定義変換演算子が 1 つだけ含まれている場合、これは最も具体的な変換演算子です。 - それ以外の場合、
U
Sₓ
からTₓ
に変換するリフト変換演算子が 1 つだけ含まれている場合、これは最も具体的な変換演算子です。 - それ以外の場合、変換があいまいになり、コンパイル時エラーが発生します。
- U に、
- 最後に、変換を適用します。
E
に型Sₓ
がない場合は、E からSₓ
への標準的な明示的な変換が実行されます。- 最も具体的なユーザー定義変換演算子は、
Sₓ
からTₓ
に変換するために呼び出されます。 Tₓ
がT
されていない場合は、Tₓ
からT
への標準的な明示的な変換が実行されます。
型S
から型へのユーザー定義の明示的な変換は、T
型からS
へのユーザー定義の明示的な変換が存在する場合に存在T
。
10.6 null 許容型を含む変換
10.6.1 Null 許容変換
null 許容変換 では、null 非許容値型に対して動作する定義済みの変換を、それらの型の null 許容形式でも使用できます。 null 非許容値型 S
から null 非許容値型に変換する定義済みの暗黙的または明示的な変換ごとに、 T
(§10.2.2、 §10.2.3、 §10.2.4、 §10.2.11、 §10.3.2 および §10.3.3)、次の null 許容変換が存在します。
S?
から暗黙的または明示的な変換T?
S
から暗黙的または明示的な変換T?
S?
からT
への明示的な変換。
null 許容変換自体は、暗黙的または明示的な変換として分類されます。
特定の null 許容変換は標準変換として分類され、ユーザー定義変換の一部として発生する可能性があります。 具体的には、すべての暗黙的な null 許容変換は標準の暗黙的な変換 (§10.4.2) として分類され、 §10.4.3 の要件を満たす明示的な null 許容変換は、標準の明示的な変換として分類されます。
S
からT
への基になる変換に基づく null 許容変換の評価は、次のように進みます。
- null 許容変換が
S?
からT?
への変換の場合:- ソース値が null (
HasValue
プロパティがfalse
) の場合、結果はT?
型の null 値になります。 - それ以外の場合、変換は、
S?
からS
へのラップ解除、S
からT
への基になる変換、T
からT?
への折り返しとして評価されます。
- ソース値が null (
- null 許容変換が
S
からT?
への変換の場合、S
からT
への基になる変換として評価され、その後にT
からT?
への折り返しが続きます。 - null 許容変換が
S?
からT
への変換の場合、変換はS?
からS
へのラップ解除として評価され、その後に基になるS
からT
への変換が行われます。
10.6.2 リフトされたコンバージョン
null 非許容値型から null 非許容値型に変換するユーザー定義変換演算子S
T
、リフトされた変換演算子はS?
からT?
に変換します。 このリフトされた変換演算子は、 S?
から S
へのラップ解除を実行し、その後にユーザー定義の S
から T
への変換を実行した後、 T
から T?
へのラップを実行します。ただし、null 値 S?
が null 値の T?
に直接変換される点が異なります。 リフト変換演算子には、基になるユーザー定義変換演算子と同じ暗黙的または明示的な分類があります。
10.7 匿名関数の変換
10.7.1 全般
anonymous_method_expressionまたはlambda_expressionは匿名関数 (§12.19) として分類されます。 式には型はありませんが、互換性のあるデリゲート型に暗黙的に変換できます。 一部のラムダ式は、互換性のある式ツリー型に暗黙的に変換される場合もあります。
具体的には、匿名関数 F
は、提供 D
デリゲート型と互換性があります。
F
にanonymous_function_signatureが含まれている場合、D
とF
は同じ数のパラメーターを持っています。F
にanonymous_function_signatureが含まれていない場合、D
は、D
のパラメーターが出力パラメーターでない限り、任意の型の 0 個以上のパラメーターを持つことができます。F
に明示的に型指定されたパラメーター リストがある場合、D
内の各パラメーターは、F
の対応するパラメーターと同じ修飾子を持ち、F
の対応するパラメーター間に ID 変換が存在します。F
に暗黙的に型指定されたパラメーター リストがある場合、D
には参照パラメーターまたは出力パラメーターがありません。F
の本体が式で、D
void 戻り値の型がor でない場合F
は非同期であり、D
は«TaskType»
戻り値の型 (§15.15.1) を持ち、F
の各パラメーターに対応するパラメーターの型がD
に与えられるとき、F
の本体は、statement_expression (§13.7) として許可される有効な式 (w.r.t §12) です。F
の本体がブロックの場合、 またD
void 戻り値の型orF
は非同期であり、D
は«TaskType»
戻り値の型を持ち、F
の各パラメーターにD
の対応するパラメーターの型が与えられると、F
の本体は有効なブロック (w.r.t §13.3) であり、return
ステートメントで式を指定しません。F
の本体が式であり、F
が非非同期であり、D
がvoid
以外の戻り値の型T
を持つ場合、 orF
は非同期であり、D
には«TaskType»<T>
戻り値の型 (§15.15.1) があり、F
の各パラメーターにD
の対応するパラメーターの型が与えられると、F
の本体は有効な式 (w.r.t §12) であり、暗黙的にT
に変換できます。F
の本体がブロックであり、F
が非非同期で、D
が void 以外の戻り値の型T
を持つ場合、orF
は async であり、D
は«TaskType»<T>
戻り値の型を持ちます。F
の各パラメーターにD
の対応するパラメーターの型が指定されている場合、F
の本体は有効なステートメント ブロック (w.r.t §13.3) で、到達不能なエンドポイントを持ち、各 return ステートメントは暗黙的にT
に変換可能な式を指定します。
例: 次の例は、これらの規則を示しています。
delegate void D(int x); D d1 = delegate { }; // Ok D d2 = delegate() { }; // Error, signature mismatch D d3 = delegate(long x) { }; // Error, signature mismatch D d4 = delegate(int x) { }; // Ok D d5 = delegate(int x) { return; }; // Ok D d6 = delegate(int x) { return x; }; // Error, return type mismatch delegate void E(out int x); E e1 = delegate { }; // Error, E has an output parameter E e2 = delegate(out int x) { x = 1; }; // Ok E e3 = delegate(ref int x) { x = 1; }; // Error, signature mismatch delegate int P(params int[] a); P p1 = delegate { }; // Error, end of block reachable P p2 = delegate { return; }; // Error, return type mismatch P p3 = delegate { return 1; }; // Ok P p4 = delegate { return "Hello"; }; // Error, return type mismatch P p5 = delegate(int[] a) // Ok { return a[0]; }; P p6 = delegate(params int[] a) // Error, params modifier { return a[0]; }; P p7 = delegate(int[] a) // Error, return type mismatch { if (a.Length > 0) return a[0]; return "Hello"; }; delegate object Q(params int[] a); Q q1 = delegate(int[] a) // Ok { if (a.Length > 0) return a[0]; return "Hello"; };
end の例
例: 次の例では、
Func<A,R>
型の引数を受け取り、A
型の値を返す関数を表すジェネリック デリゲート型R
を使用します。delegate R Func<A,R>(A arg);
割り当て
Func<int,int> f1 = x => x + 1; // Ok Func<int,double> f2 = x => x + 1; // Ok Func<double,int> f3 = x => x + 1; // Error Func<int, Task<int>> f4 = async x => x + 1; // Ok
各匿名関数のパラメーターと戻り値の型は、匿名関数が割り当てられている変数の型から決定されます。
最初の代入では、匿名関数がデリゲート型
Func<int,int>
に正常に変換されます。これは、x
が型int
指定されると、x + 1
は型int
に暗黙的に変換できる有効な式であるためです。同様に、2 番目の代入では、匿名関数がデリゲート型 Func<int,double> に正常に変換されます。これは、(
x + 1
型の)int
の結果が暗黙的にdouble
型に変換できるためです。ただし、3 番目の割り当てはコンパイル時エラーです。これは、
x
が型double
指定された場合、(x + 1
型の)double
の結果が型int
に暗黙的に変換できないためです。4 番目の代入では、(
Func<int, Task<int>>
型の)x + 1
の結果が、戻り値の型int
を持つ非同期ラムダの有効な戻り値の型int
に暗黙的に変換できるため、匿名の非同期関数がデリゲート型Task<int>
に正常に変換されます。end の例
ラムダ式F
は、デリゲート型のExpression<D>
と互換性がある場合F
D
式ツリー型と互換性があります。 これは匿名メソッドには適用されず、ラムダ式にのみ適用されます。
匿名関数は、オーバーロードの解決に影響を与え、型推論に参加する可能性があります。 詳細については、 §12.6 を参照してください。
10.7.2 デリゲート型への匿名関数変換の評価
匿名関数をデリゲート型に変換すると、匿名関数と、評価時にアクティブになっているキャプチャされた外部変数の (空の可能性がある) セットを参照するデリゲート インスタンスが生成されます。 デリゲートが呼び出されると、匿名関数の本体が実行されます。 本文内のコードは、デリゲートによって参照されるキャプチャされた外部変数のセットを使用して実行されます。 匿名メソッドをデリゲート型に変換するための代替構文として、 delegate_creation_expression (§12.8.17.6) を使用できます。
匿名関数から生成されたデリゲートの呼び出しリストには、1 つのエントリが含まれています。 デリゲートの正確なターゲット オブジェクトとターゲット メソッドは指定されていません。 特に、デリゲートのターゲット オブジェクトが 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));
...
}
}
2 つの匿名関数デリゲートはキャプチャされた外部変数の同じ (空の) セットを持ち、匿名関数は意味的に同じであるため、コンパイラはデリゲートが同じターゲット メソッドを参照することを許可されます。 実際、コンパイラは、両方の匿名関数式からまったく同じデリゲート インスタンスを返すのが許可されています。
10.7.3 ラムダ式から式ツリー型への変換の評価
ラムダ式を式ツリー型に変換すると、式ツリーが生成されます (§8.6)。 より正確には、ラムダ式変換の評価では、ラムダ式自体の構造を表すオブジェクト構造が生成されます。
すべてのラムダ式を式ツリー型に変換できるわけではありません。 互換性のあるデリゲート型への変換は常に existsが、実装で定義された理由によりコンパイル時に失敗する可能性があります。
注: ラムダ式が式ツリー型に変換できない一般的な理由は次のとおりです。
- これは、ブロック本体を持っています
async
修飾子があります- 代入演算子が含まれています
- 出力または参照パラメーターが含まれています
- 動的にバインドされた式が含まれています
end note
10.8 メソッドグループの変換
メソッド グループ (§12.2) から互換性のあるデリゲート型 (§20.4) への暗黙的な変換が存在します。 D
がデリゲート型で、E
がメソッド グループとして分類される式である場合、 D
は、次に示すように、E
に通常の形式 (E
) で適用できるメソッドが少なくとも 1 つ含まれている場合にのみ、と互換性があります (§12.6.2D
)。
メソッド グループ E
からデリゲート型への変換のコンパイル時のアプリケーション D
次に説明します。
- フォーム
M
のメソッド呼び出し (§12.8.10.2) に対応する 1 つのメソッドE(A)
が選択され、次の変更が加えられます。A
引数リストは式のリストであり、それぞれ変数として分類され、in
のout
内の対応するパラメーターの型と修飾子 (ref
、、またはD
) を使用します。ただし、dynamic
型のパラメーターを除きます。対応する式には、object
ではなく型dynamic
があります。- 考慮される候補メソッドは、通常の形式で適用可能で、省略可能なパラメーター (§12.6.4.2) を省略しないメソッドのみです。 したがって、候補メソッドは、展開されたフォームでのみ適用できる場合、または 1 つ以上の省略可能なパラメーターに対応するパラメーターが
D
に含まれていない場合は無視されます。
- §12.8.10.2 のアルゴリズムで、
M
と互換性がある (§20.4) 1 つの最適なメソッドD
が生成される場合、変換は存在すると見なされます。 - 選択したメソッド
M
がインスタンス メソッドの場合、E
に関連付けられているインスタンス式によってデリゲートのターゲット オブジェクトが決定されます。 - 選択したメソッド
M
が、インスタンス式のメンバー アクセスによって示される拡張メソッドである場合、そのインスタンス式によってデリゲートのターゲット オブジェクトが決定されます。 - 変換の結果は、
D
型の値、つまり、選択したメソッドとターゲット オブジェクトを参照するデリゲートです。
例: メソッド グループの変換を次に示します。
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
への割り当てでは、デリゲートとメソッドのパラメーターと戻り値の型が参照型でのみ異なることを許可する方法を示します。end の例
他のすべての暗黙的および明示的な変換と同様に、キャスト演算子を使用して特定の変換を明示的に実行できます。
例: したがって、例
object obj = new EventHandler(myDialog.OkClick);
代わりに書き込むことができる
object obj = (EventHandler)myDialog.OkClick;
end の例
メソッド グループ変換では、 E
内で型引数を明示的に指定するか、型推論を使用してジェネリック メソッドを参照できます (§12.6.3)。 型推論を使用する場合、デリゲートのパラメーター型は推論プロセスで引数型として使用されます。 デリゲートの戻り値の型は、推論には使用されません。 型引数が指定されているか推論されているかに関係なく、これらはメソッド グループ変換プロセスの一部です。これらは、結果のデリゲートが呼び出されたときにターゲット メソッドを呼び出すために使用される型引数です。
例:
delegate int D(string s, int i); delegate int E(); class X { public static T F<T>(string s, T t) {...} public static T G<T>() {...} static void Main() { D d1 = F<int>; // Ok, type argument given explicitly D d2 = F; // Ok, int inferred as type argument E e1 = G<int>; // Ok, type argument given explicitly E e2 = G; // Error, cannot infer from return type } }
end の例
メソッド グループは、オーバーロードの解決に影響を与え、型推論に参加する場合があります。 詳細については、 §12.6 を参照してください。
メソッド グループ変換の実行時の評価は、次のように行われます。
- コンパイル時に選択されたメソッドがインスタンス メソッドである場合、またはインスタンス メソッドとしてアクセスされる拡張メソッドである場合、デリゲートのターゲット オブジェクトは、
E
に関連付けられたインスタンス式から決定されます。- インスタンス式が評価されます。 この評価によって例外が発生した場合、それ以上の手順は実行されません。
- インスタンス式が reference_typeの場合、インスタンス式によって計算された値がターゲット オブジェクトになります。 選択したメソッドがインスタンス メソッドであり、ターゲット オブジェクトが
null
場合、System.NullReferenceException
がスローされ、それ以上の手順は実行されません。 - インスタンス式が value_typeの場合、値をオブジェクトに変換するためにボックス化操作 (§10.2.9) が実行され、このオブジェクトがターゲット オブジェクトになります。
- それ以外の場合、選択したメソッドは静的メソッド呼び出しの一部であり、デリゲートのターゲット オブジェクトは
null
。 - 次のように、コンパイル時に決定されたメソッドへの参照と、上記で計算されたターゲット オブジェクトへの参照を使用して、デリゲート型
D
のデリゲート インスタンスが取得されます。- この変換は、これらの参照が既に含まれている既存のデリゲート インスタンスを使用することが許可されます (必須ではありません)。
- 既存のインスタンスが再利用されなかった場合は、新しいインスタンスが作成されます (§20.5)。 新しいインスタンスを割り当てるのに十分なメモリがない場合は、
System.OutOfMemoryException
がスローされます。 それ以外の場合、インスタンスは指定された参照で初期化されます。
ECMA C# draft specification