16.1 全般
構造体は、データ メンバーと関数メンバーを含むことができるデータ構造を表すという点でクラスに似ています。 ただし、クラスとは異なり、構造体は値型であり、ヒープ割り当てを必要としません。
struct型の変数にはstructのデータが直接含まれますが、クラス型の変数にはデータへの参照が含まれます。後者はオブジェクトと呼ばれます。
注: 構造体は、値セマンティクスを持つ小さなデータ構造に特に役立ちます。 構造体の主な例としては、複素数、座標系のポイント、ディクショナリのキーと値のペアなどがあります。 これらのデータ構造の重要な点は、データ メンバーが少なく、継承や参照セマンティクスを使用する必要がないため、代入が参照ではなく値をコピーする値セマンティクスを使用して便利に実装できることです。 注釈
§8.3.5 で説明されているように、int、double、boolなど、C# によって提供される単純型は、実際にはすべての構造体型です。
16.2 構造体宣言
16.2.1 全般
struct_declarationは、新しい構造体を宣言するtype_declaration (§14.7) です。
struct_declaration
: attributes? struct_modifier* 'ref'? 'partial'? 'struct'
identifier type_parameter_list? struct_interfaces?
type_parameter_constraints_clause* struct_body ';'?
;
struct_declarationは、オプションの属性セット (§23) と、オプションの一連のstruct_modifier (§16.2.2)、オプションのref修飾子 (§16.2.3)、続いてオプションの部分修飾子 (§15.2.7)、キーワード struct、構造体を名前付けする識別子で構成されます。 その後に省略可能なtype_parameter_list仕様 (§15.2.3) が続き、その後に省略可能なstruct_interfaces仕様 (§16.2.5)、オプションの type_parameter_constraints 句仕様 (§15.2.5) の後に、struct_body (§16.2.6) が続き、必要に応じてセミコロンが続きます。
構造体宣言は、type_parameter_listを提供しない限り、type_parameter_constraints_clauseを提供してはならない。
type_parameter_listを提供する構造体宣言は、ジェネリック構造体宣言です。 さらに、ジェネリック クラス宣言またはジェネリック構造体宣言内に入れ子になった構造体は、それ自体がジェネリック構造体宣言です。これは、包含型の型引数を指定して構築型 (§8.4) を作成する必要があるためです。
ref キーワードを含む構造体宣言には、struct_interfaces部分は含まれません。
16.2.2 構造体修飾子
struct_declarationには、必要に応じて一連のstruct_modifierを含めることができます。
struct_modifier
: 'new'
| 'public'
| 'protected'
| 'internal'
| 'private'
| 'readonly'
| unsafe_modifier // unsafe code support
;
unsafe_modifier (§24.2) は安全でないコード (§24) でのみ使用できます。
構造体宣言で同じ修飾子が複数回出現するのはコンパイル時エラーです。
readonlyを除き、構造体宣言の修飾子はクラス宣言の修飾子と同じ意味を持ちます (§15.2.2)。
readonly修飾子は、インスタンスが変更できない型をstruct_declarationが宣言することを示します。
読み取り専用構造体には、次の制約があります。
- 各インスタンス フィールドも
readonly宣言する必要があります。 - フィールドに似たイベント (§15.8.2) を宣言することはできません。
読み取り専用構造体のインスタンスがメソッドに渡されると、その this は入力引数/パラメーターのように扱われます。これは、インスタンス フィールドへの書き込みアクセスを禁止します (コンストラクターを除く)。
16.2.3 Ref 修飾子
ref修飾子は、struct_declarationが実行スタックにインスタンスが割り当てられている型を宣言することを示します。 これらの型は、 ref 構造体 型と呼ばれます。
ref修飾子は、インスタンスに ref のようなフィールドが含まれていることを宣言し、セーフ コンテキスト (§16.4.15) からコピーすることはできません。 ref 構造体の安全なコンテキストを決定するための規則については、 §16.4.15 を参照してください。
ref 構造体型が次のいずれかのコンテキストで使用されている場合、コンパイル時エラーになります。
- 配列の要素型として。
-
ref修飾子を持たないクラスまたは構造体のフィールドの宣言された型。 -
System.ValueTypeまたはSystem.Objectにボックス化されています。 - 型引数として。
- タプル要素の型として。
- 非同期メソッド。
- 反復子。
-
ref struct型から型objectまたは型System.ValueTypeへの変換はありません。 -
ref struct型は、インターフェイスを実装するために宣言することはできません。 -
objectまたはSystem.ValueTypeで宣言されているが、ref struct型ではオーバーライドされないインスタンス メソッドは、そのref struct型のレシーバーでは呼び出されません。 -
ref struct型のインスタンス メソッドは、デリゲート型へのメソッド グループ変換によってキャプチャされません。 - ref 構造体は、ラムダ式またはローカル関数によってキャプチャされません。
注:暗黙的な
ref structパラメーターはそれらのコンテキストで使用できないため、asyncはインスタンス メソッドyield return宣言したり、インスタンス メソッド内でyield breakまたはthisステートメントを使用したりすることはできません。 注釈
これらの制約により、 ref struct 型の変数が、無効になったスタック メモリまたは無効になった変数を参照しないようにします。
16.2.4 部分修飾子
partial修飾子は、このstruct_declarationが部分型宣言であることを示します。 外側の名前空間または型宣言内で同じ名前の複数の部分構造体宣言が結合され、 §15.2.7 で指定された規則に従って 1 つの構造体宣言が形成。
16.2.5 構造体インターフェイス
構造体宣言には struct_interfaces 仕様を含めることができます。その場合、構造体は指定されたインターフェイス型を直接実装すると言われます。 ジェネリック型宣言 (§15.3.9.7) 内で宣言された入れ子になった型を含む、構築された構造体型の場合、実装された各インターフェイス型は、指定されたインターフェイス内の各 type_parameter に対して、構築された型の対応する type_argument を置き換えることによって取得されます。
struct_interfaces
: ':' interface_type_list
;
部分構造体宣言 (§15.2.7) の複数の部分でのインターフェイスの処理については、 §15.2.4.3 で詳しく説明します。
インターフェイスの実装については、 §19.6 で詳しく説明します。
16.2.6 構造体本体
構造体の struct_body は、構造体のメンバーを定義します。
struct_body
: '{' struct_member_declaration* '}'
;
16.3 構造体メンバー
構造体のメンバーは、その struct_member_declarationによって導入されたメンバーと、型 System.ValueTypeから継承されたメンバーで構成されます。
struct_member_declaration
: constant_declaration
| field_declaration
| method_declaration
| property_declaration
| event_declaration
| indexer_declaration
| operator_declaration
| constructor_declaration
| static_constructor_declaration
| type_declaration
| fixed_size_buffer_declaration // unsafe code support
;
fixed_size_buffer_declaration (§24.8.2) は安全でないコード (§24) でのみ使用できます。
注: finalizer_declarationを除くすべての種類のclass_member_declarationもstruct_member_declarationです。 注釈
§16.4 に記載されている相違点を除き、§15.3 から §15.12 で提供されるクラス メンバーの説明も構造体メンバーにも適用されます。
16.4 クラスと構造体の違い
16.4.1 全般
構造体は、いくつかの重要な方法でクラスと異なります。
- 構造体は値型です (§16.4.2)。
- すべての構造体型は、クラス
System.ValueTypeから暗黙的に継承されます (§16.4.3)。 - 構造体型の変数に代入すると、割り当てられている値の コピー が作成されます (§16.4.4)。
- 構造体の既定値は、すべてのフィールドを既定値 (§16.4.5) に設定することによって生成される値です。
- ボックス化操作とボックス化解除操作は、構造体型と特定の参照型 (§16.4.6) の間で変換するために使用されます。
-
thisの意味は、構造体メンバー内で異なります (§16.4.7)。 - 構造体のインスタンス フィールド宣言には、変数初期化子 (§16.4.8) を含められません。
- 構造体は、パラメーターなしのインスタンス コンストラクター (§16.4.9) を宣言することはできません。
- 構造体はファイナライザーを宣言できません。
- イベント宣言、プロパティ宣言、プロパティ アクセサー、インデクサー宣言、およびメソッド宣言は修飾子を
readonlyすることが許可されますが、通常、クラス内の同じメンバーの種類に対しては許可されません。
16.4.2 値セマンティクス
構造体は値型 (§8.3) であり、値セマンティクスがあると言います。 一方、クラスは参照型 (§8.2) であり、参照セマンティクスを持っていると言います。
構造体型の変数には構造体のデータが直接含まれますが、クラス型の変数にはデータを含むオブジェクトへの参照が含まれます。 構造体BA型のインスタンス フィールドが含まれており、Aが構造体型である場合、AがBまたはBから構築された型に依存するのはコンパイル時エラーです。 構造体 X に直接依存します。 この定義を考えると、構造体が依存する構造体の完全なセットは、 間接的に依存 関係の推移的なクロージャです。
例:
struct Node { int data; Node next; // error, Node directly depends on itself }は、
Nodeに独自の型のインスタンス フィールドが含まれているため、エラーです。 別の例struct A { B b; } struct B { C c; } struct C { A a; }は、各型
A、B、およびCが互いに依存するため、エラーです。end の例
クラスを使用すると、2 つの変数が同じオブジェクトを参照できるため、1 つの変数に対する操作が他の変数によって参照されるオブジェクトに影響を与える可能性があります。 構造体では、変数にはそれぞれデータの独自のコピーがあります (参照パラメーターの場合を除く)。一方の操作が他方に影響を与えることはできません。 さらに、明示的に null 許容 (§8.3.12) を除き、構造体型の値を nullすることはできません。
注: 構造体に参照型のフィールドが含まれている場合、参照されるオブジェクトの内容は他の操作によって変更できます。 ただし、フィールド自体の値 (つまり、参照するオブジェクト) は、異なる構造体値の変更によって変更することはできません。 注釈
例: 次の場合
struct Point { public int x, y; public Point(int x, int y) { this.x = x; this.y = y; } } class A { static void Main() { Point a = new Point(10, 10); Point b = a; a.x = 100; Console.WriteLine(b.x); } }出力が
10。aへのbの割り当てによって値のコピーが作成されるため、bはa.xへの割り当ての影響を受けません。Pointクラスとして宣言されている場合、100とaが同じオブジェクトを参照するため、出力はbされます。end の例
16.4.3 継承
すべての構造体型は、クラス System.ValueTypeから暗黙的に継承され、クラス objectから継承されます。 構造体宣言では実装されているインターフェイスの一覧を指定できますが、構造体宣言で基底クラスを指定することはできません。
構造体型は決して抽象型でなく、常に暗黙的にシールされます。 したがって、 abstract および sealed 修飾子は構造体宣言では許可されません。
構造体の継承はサポートされていないため、構造体メンバーの宣言されたアクセシビリティを protected、 private protected、または protected internalすることはできません。
構造体内の関数メンバーを抽象または仮想にすることはできません。また、 override 修飾子は、 System.ValueTypeから継承されたメソッドのオーバーライドのみを許可します。
16.4.4 割り当て
構造体型の変数に代入すると、割り当てられている値の コピー が作成されます。 これは、参照をコピーするが、参照によって識別されるオブジェクトをコピーするクラス型の変数への代入とは異なります。
代入と同様に、構造体が値パラメーターとして渡されるか、関数メンバーの結果として返されるときに、構造体のコピーが作成されます。 構造体は、参照渡しパラメーターを使用して、関数メンバーへの参照によって渡すことができます。
構造体のプロパティまたはインデクサーが代入のターゲットである場合、プロパティまたはインデクサー アクセスに関連付けられているインスタンス式は変数として分類されます。 インスタンス式が値として分類されると、コンパイル時エラーが発生します。 詳細については、 §12.23.2 を参照してください。
16.4.5 既定値
§9.3 で説明されているように、いくつかの種類の変数は、作成時に自動的に既定値に初期化されます。 クラス型とその他の参照型の変数の場合、この既定値は null。 ただし、構造体は nullできない値型であるため、構造体の既定値は、すべての値型フィールドを既定値に設定し、すべての参照型フィールドを nullすることによって生成される値です。
例: 上記で宣言した
Point構造体を参照しています。Point[] a = new Point[100];は、配列内の各
Pointを、xフィールドとyフィールドを 0 に設定して生成された値に初期化します。end の例
構造体の既定値は、構造体の既定のコンストラクターによって返される値に対応します (§8.3.3)。 クラスとは異なり、構造体はパラメーターなしのインスタンス コンストラクターを宣言できません。 代わりに、すべての構造体に暗黙的にパラメーターなしのインスタンス コンストラクターがあり、すべてのフィールドを既定値に設定した結果の値を常に返します。
注: 構造体は、既定の初期化状態を有効な状態と見なすために設計する必要があります。 この例では、
struct KeyValuePair { string key; string value; public KeyValuePair(string key, string value) { if (key == null || value == null) { throw new ArgumentException(); } this.key = key; this.value = value; } }ユーザー定義インスタンス コンストラクターは、明示的に呼び出された場合にのみ、
null値から保護します。KeyValuePair変数が既定値の初期化の対象となる場合、keyフィールドとvalueフィールドはnullされ、構造体はこの状態を処理するように準備する必要があります。注釈
16.4.6 ボックス化とボックス化解除
クラス型の値は、コンパイル時に参照を別の型として扱うだけで、 object 型またはクラスによって実装されるインターフェイス型に変換できます。 同様に、 object 型の値またはインターフェイス型の値は、参照を変更せずにクラス型に変換できます (ただし、この場合は実行時の型チェックが必要です)。
構造体は参照型ではないため、これらの操作は構造体型に対して異なる方法で実装されます。 構造体型の値が ( §10.2.9 で定義されている) 特定の参照型に変換されるとボックス化操作が行われます。 同様に、( §10.3.7 で定義されている) 特定の参照型の値が構造体型に変換されると、ボックス化解除操作が実行されます。 クラス型に対する同じ操作との主な違いは、ボックス化とボックス化解除の スコープ ボックス化されたインスタンスとの間の構造体値です。
注: したがって、ボックス化またはボックス化解除操作の後、ボックス化されていない
structに加えられた変更は、ボックス化されたstructには反映されません。 注釈
ボックス化とボックス化解除の詳細については、 §10.2.9 および §10.3.7 を参照してください。
16.4.7 この意味
構造体内のthisの意味は、§12.8.14 で説明されているように、クラス内のthisの意味とは異なります。 構造体型が System.ValueType から継承された仮想メソッド ( Equals、 GetHashCode、 ToStringなど) をオーバーライドする場合、構造体型のインスタンスを介して仮想メソッドを呼び出しても、ボックス化は発生しません。 これは、構造体が型パラメーターとして使用され、型パラメーター型のインスタンスを介して呼び出しが発生した場合でも当てはまります。
例:
struct Counter { int value; public override string ToString() { value++; return value.ToString(); } } class Program { static void Test<T>() where T : new() { T x = new T(); Console.WriteLine(x.ToString()); Console.WriteLine(x.ToString()); Console.WriteLine(x.ToString()); } static void Main() => Test<Counter>(); }プログラムの出力は次のとおりです。
1 2 3
ToStringが副作用を与えるのは不適切なスタイルですが、この例では、x.ToString()の 3 回の呼び出しでボックス化が発生しなかったことを示しています。end の例
同様に、メンバーが値型内に実装されている場合、制約付き型パラメーターのメンバーにアクセスするときにボックス化が暗黙的に行われることはありません。 たとえば、インターフェイス ICounter に、値の変更に使用できるメソッド Incrementが含まれているとします。
ICounterが制約として使用されている場合、Increment メソッドの実装は、ボックス化されたコピーIncrement呼び出された変数への参照を使用して呼び出されます。
例:
interface ICounter { void Increment(); } struct Counter : ICounter { int value; public override string ToString() => value.ToString(); void ICounter.Increment() => value++; } class Program { static void Test<T>() where T : ICounter, new() { T x = new T(); Console.WriteLine(x); x.Increment(); // Modify x Console.WriteLine(x); ((ICounter)x).Increment(); // Modify boxed copy of x Console.WriteLine(x); } static void Main() => Test<Counter>(); }
Incrementを最初に呼び出すと、変数xの値が変更されます。 これは、ボックス化されたIncrementのコピーの値を変更する、xの 2 回目の呼び出しと同じではありません。 したがって、プログラムの出力は次のようになります。0 1 1end の例
16.4.8 フィールド初期化子
§16.4.5 で説明されているように、構造体の既定値は、すべての値型フィールドを既定値に設定し、すべての参照型フィールドをnullに設定した結果の値で構成されます。 このため、構造体では、インスタンス フィールド宣言に変数初期化子を含めないようにします。 この制限は、インスタンス フィールドにのみ適用されます。 構造体の静的フィールドには、変数初期化子を含めることができます。
例: 次
struct Point { public int x = 1; // Error, initializer not permitted public int y = 1; // Error, initializer not permitted }は、インスタンス フィールド宣言に変数初期化子が含まれているため、エラーになります。
end の例
field_declarationがstruct_declaration内で直接宣言され、struct_modifierreadonlyを持つ場合は、field_modifierreadonlyを持つ必要があります。
16.4.9 コンストラクター
クラスとは異なり、構造体はパラメーターなしのインスタンス コンストラクターを宣言できません。 代わりに、すべての構造体に暗黙的にパラメーターなしのインスタンス コンストラクターがあり、すべての値型フィールドを既定値に設定し、すべての参照型フィールドを null に設定した結果の値を常に返します (§8.3.3)。 構造体は、パラメーターを持つインスタンス コンストラクターを宣言できます。
例: 次の場合
struct Point { int x, y; public Point(int x, int y) { this.x = x; this.y = y; } } class A { static void Main() { Point p1 = new Point(); Point p2 = new Point(0, 0); } }ステートメントは両方とも、
Pointを持つxを作成し、yゼロに初期化します。end の例
構造体インスタンス コンストラクターは、フォーム base(argument_list)のコンストラクター初期化子を含められません。ここで、 argument_list は省略可能です。
構造体インスタンス コンストラクターの this パラメーターは、構造体型の出力パラメーターに対応します。 そのため、 this は、コンストラクターが返すすべての場所に確実に割り当てられます (§9.4)。 同様に、確実に割り当てられる前に、コンストラクター本体で読み取ることはできません (暗黙的であっても)。
構造体インスタンス コンストラクターでコンストラクター初期化子が指定されている場合、その初期化子は、コンストラクターの本体より前に発生する、これに対する明確な割り当てと見なされます。 したがって、本体自体には初期化要件はありません。
例: 以下のインスタンス コンストラクターの実装について考えてみましょう。
struct Point { int x, y; public int X { set { x = value; } } public int Y { set { y = value; } } public Point(int x, int y) { X = x; // error, this is not yet definitely assigned Y = y; // error, this is not yet definitely assigned } }構築される構造体のすべてのフィールドが確実に割り当てられるまで、インスタンス関数メンバー (プロパティ
XおよびYの set アクセサーを含む) を呼び出すことができます。 ただし、Pointが構造体ではなくクラスである場合は、インスタンス コンストラクターの実装が許可されることに注意してください。 これには 1 つの例外があり、自動的に実装されるプロパティ (§15.7.4) が含まれます。 明確な代入規則 (§12.23.2) は、その構造体型のインスタンス コンストラクター内の構造体型の自動プロパティへの割り当てを明示的に除外します。このような割り当ては、自動プロパティの隠しバッキング フィールドの明確な割り当てと見なされます。 したがって、次の操作が許可されます。struct Point { public int X { get; set; } public int Y { get; set; } public Point(int x, int y) { X = x; // allowed, definitely assigns backing field Y = y; // allowed, definitely assigns backing field } }例終わり]
16.4.10 静的コンストラクター
構造体の静的コンストラクターは、クラスの場合とほとんど同じ規則に従います。 構造体型の静的コンストラクターの実行は、アプリケーション ドメイン内で発生する次のイベントの最初のイベントによってトリガーされます。
- 構造体型の静的メンバーが参照されます。
- 構造体型の明示的に宣言されたコンストラクターが呼び出されます。
注: 構造体型の既定値 (§16.4.5) を作成しても、静的コンストラクターはトリガーされません。 (この例は、配列内の要素の初期値です)。 メモの送信
16.4.11 プロパティ
インスタンス プロパティのstruct_declaration 内のproperty_declaration (§15.7.1) には property_modifierreadonly が含まれている場合があります。 ただし、静的プロパティには、その修飾子は含まれません。
その構造体で宣言された読み取り専用プロパティを使用してインスタンス構造体変数の状態を変更しようとすると、コンパイル時エラーになります。
readonly修飾子を持つ自動実装プロパティがsetアクセサーも持つことは、コンパイル時エラーの原因になります。
readonly アクセサーを持つset構造体に自動的に実装されるプロパティのコンパイル時エラーです。
readonly アクセサーは暗黙的に読み取り専用と見なされるため、readonly構造体内で宣言された自動的に実装されるプロパティには、get修飾子は必要ありません。
プロパティ自体だけでなく、readonlyアクセサーとgetアクセサーに対してset修飾子を持つことは、コンパイル時エラーです。
プロパティがすべてのアクセサーに対して読み取り専用修飾子を持つことはコンパイル時エラーです。
注: エラーを修正するには、アクセサーからプロパティ自体に修飾子を移動します。 注釈
プロパティ アクセサー式の場合は、次の s.P。
-
s.PのプロセスがMの一時的なコピーを作成するときに、Tが型の set アクセサーsを呼び出すと、コンパイル時エラーになります。 -
s.PT型の get アクセサーを呼び出すと、必要に応じての一時的なコピーを作成するなど、sプロセスが実行されます。
自動的に実装されるプロパティ (§15.7.4) は、プロパティ アクセサーのみがアクセスできる非表示のバッキング フィールドを使用します。
注: このアクセス制限は、自動的に実装されるプロパティを含む構造体のコンストラクターには、関数メンバーが呼び出されるか、コンストラクターが戻る前に確実に割り当てられるすべてのフィールドの要件を満たすために、明示的なコンストラクター初期化子が必要になることがよくあります。 注釈
16.4.12 メソッド
struct_declaration内のインスタンス メソッドのmethod_declaration (§15.6.1) にはmethod_modifierreadonlyを含めることができます。 ただし、静的メソッドには、その修飾子は含まれません。
その構造体で宣言された読み取り専用メソッドを使用してインスタンス構造体変数の状態を変更しようとすると、コンパイル時エラーになります。
読み取り専用メソッドは兄弟メソッド、非読み取り専用メソッド、またはプロパティまたはインデクサー get アクセサーを呼び出すことができますが、その結果、防御手段として this の暗黙的なコピーが作成されます。
読み取り専用メソッドは、読み取り専用の兄弟プロパティまたはインデクサー セット アクセサーを呼び出す場合があります。 兄弟メンバーのアクセサーが明示的または暗黙的に読み取り専用でない場合は、コンパイル エラーが発生します。
部分メソッドのすべての method_declarationは、 readonly 修飾子を持つ必要があります。または、それらのいずれも修飾子を持つ必要はありません。
16.4.13 インデクサー
struct_declaration内のインスタンス インデクサーのindexer_declaration (§15.9) には、indexer_modifierが含readonly。
その構造体で宣言された読み取り専用インデクサーを使用してインスタンス構造体変数の状態を変更しようとすると、コンパイル時エラーになります。
インデクサー自体だけでなく、readonlyアクセサーまたは get アクセサーに対してset修飾子を持つことは、コンパイル時エラーです。
インデクサーがすべてのアクセサーに対して読み取り専用修飾子を持つことはコンパイル時エラーです。
注: エラーを修正するには、アクセサーからインデクサー自体に修飾子を移動します。 注釈
16.4.14 イベント
インスタンス用のevent_declaration (§15.8.1) は、struct_declaration内の非フィールド状イベントにevent_modifierreadonlyを含む場合があります。 ただし、静的イベントには、その修飾子は含まれません。
16.4.15 安全なコンテキスト制約
16.4.15.1 全般
コンパイル時に、各式は、そのインスタンスとそのすべてのフィールドに安全にアクセスできるコンテキスト、 safe-contextに関連付けられます。 セーフ コンテキストは、式を囲むコンテキストであり、値をエスケープしても安全です。
コンパイル時の型が ref 構造体でない式には、呼び出し元コンテキストのセーフ コンテキストがあります。
default式には、任意の型に対して、呼び出し元コンテキストのセーフ コンテキストがあります。
コンパイル時の型が ref 構造体である既定以外の式の場合、次のセクションで定義されているセーフ コンテキストがあります。
セーフ コンテキストは、値がコピーされるコンテキストを記録します。 セーフ コンテキスト E1を持つ式S1から、セーフ コンテキスト E2を持つ式S2への代入を考えると、S2がS1よりも広いコンテキストである場合はエラーになります。
参照変数 (§9.7.2)、declaration-block、function-member、および caller-context に定義されている ref-safe-context 値と同じ、3 つの異なるセーフ コンテキスト値があります。 式のセーフ コンテキストでは、次のように使用が制限されます。
- return ステートメント
return e1の場合、e1のセーフ コンテキストは呼び出し元コンテキストである必要があります。 - 割り当て
e1 = e2e2のセーフ コンテキストは、少なくともe1のセーフ コンテキストと同じ幅のコンテキストである必要があります。
メソッドの呼び出しで、ref型のoutまたはref struct引数 (型がreadonlyでない限り、レシーバーを含む) がセーフ コンテキストS1の場合、引数 (レシーバーを含む) はS1よりも狭いセーフ コンテキストを持つ可能性があります。
16.4.15.2 パラメーターセーフコンテキスト
ref 構造体型のパラメーター (インスタンス メソッドの this パラメーターを含む) には、呼び出し元コンテキストのセーフ コンテキストがあります。
16.4.15.3 ローカル変数のセーフ コンテキスト
ref 構造体型のローカル変数には、次のようなセーフ コンテキストがあります。
- 変数が
foreachループの反復変数である場合、変数のセーフ コンテキストは、foreachループの式のセーフ コンテキストと同じです。 - それ以外の場合、変数の宣言に初期化子がある場合、変数のセーフ コンテキストは、その初期化子のセーフ コンテキストと同じです。
- それ以外の場合、変数は宣言の時点で初期化されておらず、呼び出し元コンテキストのセーフ コンテキストを持ちます。
16.4.15.4 フィールド セーフ コンテキスト
e.Fの型が ref 構造体型であるフィールド Fへの参照には、eのセーフ コンテキストと同じセーフ コンテキストがあります。
16.4.15.5 演算子
ユーザー定義演算子のアプリケーションは、メソッド呼び出し (§16.4.15.6) として扱われます。
e1 + e2やc ? e1 : e2などの値を生成する演算子の場合、結果のセーフ コンテキストは、演算子のオペランドのセーフ コンテキストの中で最も狭いコンテキストです。 その結果、 +eなどの値を生成する単項演算子の場合、結果のセーフ コンテキストはオペランドのセーフ コンテキストになります。
注: 条件演算子の最初のオペランドは
boolであるため、そのセーフ コンテキストは呼び出し元コンテキストです。 その後、結果として得られるセーフ コンテキストは、2 番目と 3 番目のオペランドの最も狭いセーフ コンテキストになります。 注釈
16.4.15.6 メソッドとプロパティの呼び出し
メソッド呼び出し e1.M(e2, ...) またはプロパティ呼び出し e.P の結果の値には、次のコンテキストのうち最も小さいセーフ コンテキストがあります。
- 呼び出し元コンテキスト。
- すべての引数式 (受信側を含む) のセーフ コンテキスト。
プロパティの呼び出し ( get または set) は、上記の規則によって基になるメソッドのメソッド呼び出しとして扱われます。
16.4.15.7 stackalloc
stackalloc 式の結果には、関数メンバーのセーフ コンテキストがあります。
16.4.15.8 コンストラクターの呼び出し
コンストラクターを呼び出す new 式は、構築される型を返すと見なされるメソッド呼び出しと同じ規則に従います。
さらに、任意の初期化子が存在する場合、セーフ コンテキストは、すべてのオブジェクト初期化子式のすべての引数とオペランドのセーフ コンテキストの中で最も小さい値です。
注: これらの規則は、次の形式のコンストラクターを持たない
Span<T>に依存します。public Span<T>(ref T p)このようなコンストラクターにより、
Span<T>のインスタンスがフィールドとして使用され、refフィールドと区別できなくなります。 このドキュメントで説明する安全規則は、C# または .NET で有効なコンストラクトではないrefフィールドによって異なります。 注釈
ECMA C# draft specification