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 intend の例
一部の変換は言語によって定義されます。 プログラムは、独自の変換を定義することもできます (§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.8)、代入 (§12.23) など、さまざまな状況で発生する可能性があります。
事前に定義された暗黙的な変換は常に成功し、例外がスローされることはありません。
注: 適切に設計されたユーザー定義の暗黙的な変換も、これらの特性を示す必要があります。 注釈
変換のために、 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.8) で規定されているものに精度が低下することが保証されます。
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 暗黙的な列挙変換
暗黙的な列挙変換を使用すると、任意の整数型を持つconstant_expression (§12.25) と値 0 を任意の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から任意のSTに渡されます。 - 任意のclass_type
Sから任意のinterface_typeTまで、Sを実装T提供されます。 -
が
Sから派生している場合は、任意のinterface_typeTから任意のSTに渡されます。 - 要素型がされた
SSᵢから、要素型を持つTTᵢまで、次のすべてが当てはまる場合。-
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へ。 - インターフェイスまたはデリゲート型への任意のreference_typeから
Tインターフェイスまたはデリゲート型への暗黙的な ID または参照変換T₀があり、T₀がに分散変換可能 (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に
Inon_nullable_value_typeから別のinterface_typeへのボックス化変換がありI₀は分散変換 (I₀) からに変換できます。I - 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 が出力されます。ボックス化クラスの類似点は、ボックス化の概念的な動作を示す便利なツールとして使用しないでください。 この仕様で説明されている動作と、この方法で正確に実装されたボックス化の結果として生じる動作には、多くの微妙な違いがあります。
注釈
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) は、constant_expressionの値が変換先の型の範囲内にある場合、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から、の効果的なインターフェイス セット内のIT、TからIの任意の基本インターフェイスまで。 -
Tから型パラメーターへのUTがUに依存している場合 (§15.2.5)。注:
Tは参照型であることが知られているため、Tのスコープ内では、Uがコンパイル時に参照型であることが判明していない場合でも、Uの実行時の型は常に参照型になります。 注釈 - 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参照型と呼ばれます)。 注釈 -
Tから、の効果的なインターフェイス セット内のIT、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に分散変換可能である場合 (§19.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.2.18 スイッチ式の変換
switch_expression (§12.11) からTのswitch_expression_arm_expressionからへの暗黙的な変換が存在するすべての型Tへの暗黙的な変換があります。
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.8) で行うことができます。
明示的な変換のセットには、すべての暗黙的な変換が含まれます。
注: たとえば、特定のメソッド オーバーロードを強制的に選択するために、暗黙的な ID 変換が存在する場合に明示的なキャストを使用できます。 注釈
暗黙的な変換ではない明示的な変換は、常に成功することが証明できない変換、情報が失われる可能性が知られている変換、および型のドメイン間の変換は、明示的な表記法に見合うのに十分に異なります。
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.8) を使用して、任意の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 になることはありません。 注釈
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の基になる型として扱い、結果の型間で暗黙的または明示的な数値変換を実行することによって処理されます。
例: 基底型が である
Eintの場合、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から任意のSTに渡されます。 - 要素型がされた
SSᵢから、要素型を持つTTᵢまで、次のすべてが当てはまる場合。-
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から参照型へのTSから参照型への明示的な変換があり、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) は変更されません。参照されるオブジェクトの型または値は変更されません。 注釈
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から
Inon_nullable_value_type interface_typeからnon_nullable_value_typeへのボックス化解除変換がありI₀がI₀にvariance_convertibleされるか、IがIに分散変換可能である (I₀)。 - 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;注釈
特定の 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) に依存している場合、TUから。注:
Tは参照型であることが知られているため、Tのスコープ内では、コンパイル時にUが参照型であることが判明していない場合でも、実行時の型は常に参照型になります。 注釈
参照型 (T) と呼ばれるtype_parameterの場合、Tに関連する次の変換は、コンパイル時に変換のボックス化解除 (§10.3.7) と見なされます。 実行時に、 T が値型の場合、変換はボックス化解除変換として実行されます。 実行時に、 T が参照型の場合、変換は明示的な参照変換または ID 変換として実行されます。
-
Cの有効な基底クラスTからTまで、Cの任意の基底クラスからTまで。注: C は、
System.Object、System.ValueType、またはSystem.Enumのいずれかの型になります (それ以外の場合は、T参照型と呼ばれます)。 注釈 - 任意の interface_type から
T。
参照型 (§15.2.5) であることがT知られていないtype_parameterの場合、次の明示的な変換が存在します。
-
Tからへの暗黙的な変換がまだ存在しない場合は、Iから任意のTIに変換します。 この変換は、から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に存在します。 注釈
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 ) が型から型 に存在し、 も も interface_type でない場合、は に含まれる とされ、 は を包含 とされます。 - 式 から型
Eへの標準の暗黙的な変換 (B) が存在し、BもEの型 (存在する場合) も interface_types でない場合は、EはBに包含されていると言い、Bは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 つだけ見つからない場合、変換はあいまいになり、コンパイル時エラーが発生します。
-
最も具体的な変換演算子を検索します。
-
USₓからTₓに変換するユーザー定義変換演算子が 1 つだけ含まれている場合、これは最も具体的な変換演算子です。 - それ以外の場合、
USₓから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₀がクラスの場合) で構成されます。 セットに既に含まれている別の型への 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 つだけ含まれている場合、これは最も具体的な変換演算子です。 - それ以外の場合、
USₓから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 非許容値型に変換するユーザー定義変換演算子ST、リフトされた変換演算子はS?からT?に変換します。 このリフトされた変換演算子は、 S? から S へのラップ解除を実行し、その後にユーザー定義の S から T への変換を実行した後、 T から T?へのラップを実行します。ただし、null 値 S? が null 値の T?に直接変換される点が異なります。 リフト変換演算子には、基になるユーザー定義変換演算子と同じ暗黙的または明示的な分類があります。
10.7 匿名関数の変換
10.7.1 全般
anonymous_method_expressionまたはlambda_expressionは匿名関数 (§12.21) として分類されます。 式には型はありませんが、互換性のあるデリゲート型に暗黙的に変換できます。 一部のラムダ式は、互換性のある式ツリー型に暗黙的に変換される場合もあります。
具体的には、匿名関数 F は、提供 D デリゲート型と互換性があります。
-
Fにanonymous_function_signatureが含まれている場合、DとFは同じ数のパラメーターを持っています。 -
Fにanonymous_function_signatureが含まれていない場合、Dは、Dのパラメーターが出力パラメーターでない限り、任意の型の 0 個以上のパラメーターを持つことができます。 -
Fに明示的に型指定されたパラメーター リストがある場合、D内の各パラメーターは、Fの対応するパラメーターと同じ修飾子を持ち、Fの対応するパラメーター間に ID 変換が存在します。 -
Fに暗黙的に型指定されたパラメーター リストがある場合、Dには参照パラメーターまたは出力パラメーターがありません。 -
Fの本文が、式で、いずれかDに viod の戻り値の型がある場合、またはF非同期である場合、およびDに«TaskType»戻り値の値がある場合で (§15.14.1)、Fの各パラメーターに、Dで対応するパラメーターの型が指定されると、Fの本文は、有効な式 (w.r.t §12) となり、statement_expression (§13.7) として許可されます。 -
Fの本体がブロックの場合、 またDvoid 戻り値の型orFは非同期であり、Dは«TaskType»戻り値の型を持ち、Fの各パラメーターにDの対応するパラメーターの型が与えられると、Fの本体は有効なブロック (w.r.t §13.3) であり、returnステートメントで式を指定しません。 -
Fの本文が、式で、いずれかFが、非同期ではなく、Dに 非void戻り値の型Tがあるか、 またはFが非同期で、Dに«TaskType»<T>戻り値の型がある場合で (§15.14.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>と互換性がある場合FD式ツリー型と互換性があります。 これは匿名メソッドには適用されず、ラムダ式にのみ適用されます。
匿名関数は、オーバーロードの解決に影響を与え、型推論に参加する可能性があります。 詳細については、 §12.6 を参照してください。
10.7.2 デリゲート型への匿名関数変換の評価
匿名関数をデリゲート型に変換すると、匿名関数と、評価時にアクティブになっているキャプチャされた外部変数の (空の可能性がある) セットを参照するデリゲート インスタンスが生成されます。 デリゲートが呼び出されると、匿名関数の本体が実行されます。 本文内のコードは、デリゲートによって参照されるキャプチャされた外部変数のセットを使用して実行されます。 匿名メソッドをデリゲート型に変換するための代替構文として、delegate_creation_expression (§12.8.17.5) を使用できます。
匿名関数から生成されたデリゲートの呼び出しリストには、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修飾子があります- 代入演算子が含まれています
- 出力または参照パラメーターが含まれています
- 動的にバインドされた式が含まれています
注釈
10.8 メソッドグループの変換
メソッド グループ (§12.2) から互換性のあるデリゲート型 (§21.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と互換性のある (§21.4)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のデリゲート インスタンスが取得されます。- この変換は、これらの参照が既に含まれている既存のデリゲート インスタンスを使用することが許可されます (必須ではありません)。
- 既存のインスタンスが再利用されなかった場合は、新しいインスタンスが作成されます (§21.5)。 新しいインスタンスを割り当てるのに十分なメモリがない場合は、
System.OutOfMemoryExceptionがスローされます。 それ以外の場合、インスタンスは指定された参照で初期化されます。
ECMA C# draft specification