16 構造体
16.1 全般
構造体は、データ メンバーと関数メンバーを含むことができるデータ構造を表すという点でクラスに似ています。 ただし、クラスとは異なり、構造体は値型であり、ヒープ割り当てを必要としません。 struct
型の変数にはstruct
のデータが直接含まれますが、クラス型の変数にはデータへの参照が含まれます。後者はオブジェクトと呼ばれます。
注: 構造体は、値セマンティクスを持つ小さなデータ構造に特に役立ちます。 構造体の主な例としては、複素数、座標系のポイント、ディクショナリのキーと値のペアなどがあります。 これらのデータ構造の重要な点は、データ メンバーが少なく、継承や参照セマンティクスを使用する必要がないため、代入が参照ではなく値をコピーする値セマンティクスを使用して便利に実装できることです。 end note
§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は、オプションの一連の attributes (§22) の後に、オプションの一連のstruct_modifier (§16.2.2) が続き、その後に省略可能なref
修飾子で構成されます。 (§16.2.3)、その後に省略可能な部分修飾子 (§15.2.7) が続き、その後にキーワード struct
と構造体に名前を付けた identifier が続きます。 その後に省略可能な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_clausesを提供しません。
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 (§23.2) は、安全でないコード (§23) でのみ使用できます。
構造体宣言で同じ修飾子が複数回出現するのはコンパイル時エラーです。
readonly
を除き、構造体宣言の修飾子はクラス宣言の修飾子と同じ意味を持ちます (§15.2.2)。
readonly
修飾子は、インスタンスが変更できない型をstruct_declarationが宣言することを示します。
読み取り専用構造体には、次の制約があります。
- 各インスタンス フィールドも
readonly
宣言する必要があります。 - どのインスタンス プロパティにも set_accessor_declaration (§15.7.3) はありません。
- フィールドに似たイベント (§15.8.2) を宣言することはできません。
読み取り専用構造体のインスタンスがメソッドに渡されると、その this
は入力引数/パラメーターのように扱われます。これは、インスタンス フィールドへの書き込みアクセスを禁止します (コンストラクターを除く)。
16.2.3 Ref 修飾子
ref
修飾子は、struct_declarationが実行スタックにインスタンスが割り当てられている型を宣言することを示します。 これらの型は、 ref 構造体 型と呼ばれます。 ref
修飾子は、インスタンスに ref のようなフィールドが含まれていることを宣言し、セーフ コンテキスト (§16.4.12) からコピーすることはできません。 ref 構造体の安全なコンテキストを決定するための規則については、 §16.4.12 で説明されています。
ref 構造体型が次のいずれかのコンテキストで使用されている場合、コンパイル時エラーになります。
- 配列の要素型として。
ref
修飾子を持たないクラスまたは構造体のフィールドの宣言された型。System.ValueType
またはSystem.Object
にボックス化されています。- 型引数として。
- タプル要素の型として。
- 非同期メソッド。
- 反復子。
ref struct
型から型object
または型System.ValueType
への変換はありません。ref struct
型は、インターフェイスを実装するために宣言することはできません。object
またはSystem.ValueType
で宣言されているが、ref struct
型ではオーバーライドされないインスタンス メソッドは、そのref struct
型のレシーバーでは呼び出されません。ref struct
型のインスタンス メソッドは、デリゲート型へのメソッド グループ変換によってキャプチャされません。- ref 構造体は、ラムダ式またはローカル関数によってキャプチャされません。
注:暗黙的な
this
パラメーターはそれらのコンテキストで使用できないため、ref struct
はインスタンス メソッドasync
宣言したり、インスタンス メソッド内でyield return
またはyield break
ステートメントを使用したりすることはできません。 end note
これらの制約により、 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 で詳しく説明します。
インターフェイスの実装については、 §18.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 (§23.8.2) は、安全でないコード (§23) でのみ使用できます。
注: finalizer_declarationを除くすべての種類のclass_member_declarationもstruct_member_declarationです。 end note
§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) を宣言することはできません。
- 構造体はファイナライザーを宣言できません。
16.4.2 値セマンティクス
構造体は値型 (§8.3) であり、値セマンティクスがあると言います。 一方、クラスは参照型 (§8.2) であり、参照セマンティクスを持っていると言います。
構造体型の変数には構造体のデータが直接含まれますが、クラス型の変数にはデータを含むオブジェクトへの参照が含まれます。 構造体B
A
型のインスタンス フィールドが含まれており、A
が構造体型である場合、A
がB
またはB
から構築された型に依存するのはコンパイル時エラーです。 A struct X
X
Y
型のインスタンス フィールドが含まれている場合構造体Y
に間接的に依存します。 この定義を考えると、構造体が依存する構造体の完全なセットは、 間接的に依存 関係の推移的なクロージャです。
例:
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
することはできません。
注: 構造体に参照型のフィールドが含まれている場合、参照されるオブジェクトの内容は他の操作によって変更できます。 ただし、フィールド自体の値 (つまり、参照するオブジェクト) は、異なる構造体値の変更によって変更することはできません。 end note
例: 次の場合
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
。b
へのa
の割り当てによって値のコピーが作成されるため、b
はa.x
への割り当ての影響を受けません。Point
クラスとして宣言されている場合、a
とb
が同じオブジェクトを参照するため、出力は100
されます。end の例
16.4.3 継承
すべての構造体型は、クラス System.ValueType
から暗黙的に継承され、クラス object
から継承されます。 構造体宣言では実装されているインターフェイスの一覧を指定できますが、構造体宣言で基底クラスを指定することはできません。
構造体型は決して抽象型でなく、常に暗黙的にシールされます。 したがって、 abstract
および sealed
修飾子は構造体宣言では許可されません。
構造体の継承はサポートされていないため、構造体メンバーの宣言されたアクセシビリティを protected
、 private protected
、または protected internal
することはできません。
構造体内の関数メンバーを抽象または仮想にすることはできません。また、 override
修飾子は、 System.ValueType
から継承されたメソッドのオーバーライドのみを許可します。
16.4.4 割り当て
構造体型の変数に代入すると、割り当てられている値の コピー が作成されます。 これは、参照をコピーするが、参照によって識別されるオブジェクトをコピーするクラス型の変数への代入とは異なります。
代入と同様に、構造体が値パラメーターとして渡されるか、関数メンバーの結果として返されるときに、構造体のコピーが作成されます。 構造体は、参照渡しパラメーターを使用して、関数メンバーへの参照によって渡すことができます。
構造体のプロパティまたはインデクサーが代入のターゲットである場合、プロパティまたはインデクサー アクセスに関連付けられているインスタンス式は変数として分類されます。 インスタンス式が値として分類されると、コンパイル時エラーが発生します。 これについては、 §12.21.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
され、構造体はこの状態を処理するように準備する必要があります。end note
16.4.6 ボックス化とボックス化解除
クラス型の値は、コンパイル時に参照を別の型として扱うだけで、 object
型またはクラスによって実装されるインターフェイス型に変換できます。 同様に、 object
型の値またはインターフェイス型の値は、参照を変更せずにクラス型に変換できます (ただし、この場合は実行時の型チェックが必要です)。
構造体は参照型ではないため、これらの操作は構造体型に対して異なる方法で実装されます。 構造体型の値が ( §10.2.9 で定義されている) 特定の参照型に変換されるとボックス化操作が行われます。 同様に、( §10.3.7 で定義されている) 特定の参照型の値が構造体型に変換されると、ボックス化解除操作が実行されます。 クラス型に対する同じ操作との主な違いは、ボックス化とボックス化解除の スコープ ボックス化されたインスタンスとの間の構造体値です。
注: したがって、ボックス化またはボックス化解除操作の後、ボックス化されていない
struct
に加えられた変更は、ボックス化されたstruct
には反映されません。 end note
ボックス化とボックス化解除の詳細については、 §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
の値が変更されます。 これは、ボックス化されたx
のコピーの値を変更する、Increment
の 2 回目の呼び出しと同じではありません。 したがって、プログラムの出力は次のようになります。0 1 1
end の例
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 の例
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); } }
ステートメントは両方とも、
x
を持つPoint
を作成し、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.21.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 } }
end example]
16.4.10 静的コンストラクター
構造体の静的コンストラクターは、クラスの場合とほとんど同じ規則に従います。 構造体型の静的コンストラクターの実行は、アプリケーション ドメイン内で発生する次のイベントの最初のイベントによってトリガーされます。
- 構造体型の静的メンバーが参照されます。
- 構造体型の明示的に宣言されたコンストラクターが呼び出されます。
注: 構造体型の既定値 (§16.4.5) を作成しても、静的コンストラクターはトリガーされません。 (この例は、配列内の要素の初期値です)。 メモの送信
16.4.11 自動的に実装されるプロパティ
自動的に実装されるプロパティ (§15.7.4) は、プロパティ アクセサーのみがアクセスできる非表示のバッキング フィールドを使用します。
注: このアクセス制限は、自動的に実装されるプロパティを含む構造体のコンストラクターには、関数メンバーが呼び出されるか、コンストラクターが戻る前に確実に割り当てられるすべてのフィールドの要件を満たすために、明示的なコンストラクター初期化子が必要になることがよくあります。 end note
16.4.12 セーフ コンテキスト制約
16.4.12.1 全般
コンパイル時に、各式は、そのインスタンスとそのすべてのフィールドに安全にアクセスできるコンテキスト、 safe-contextに関連付けられます。 セーフ コンテキストは、式を囲むコンテキストであり、値をエスケープしても安全です。
コンパイル時の型が ref 構造体でない式には、呼び出し元コンテキストのセーフ コンテキストがあります。
default
式には、任意の型に対して、呼び出し元コンテキストのセーフ コンテキストがあります。
コンパイル時の型が ref 構造体である既定以外の式の場合、次のセクションで定義されているセーフ コンテキストがあります。
セーフ コンテキストは、値がコピーされるコンテキストを記録します。 セーフ コンテキスト S1
を持つ式E1
から、セーフ コンテキスト S2
を持つ式E2
への代入を考えると、S2
がS1
よりも広いコンテキストである場合はエラーになります。
参照変数 (§9.7.2)、declaration-block、function-member、および caller-context に定義されている ref-safe-context 値と同じ、3 つの異なるセーフ コンテキスト値があります。 式のセーフ コンテキストでは、次のように使用が制限されます。
- return ステートメント
return e1
の場合、e1
のセーフ コンテキストは呼び出し元コンテキストである必要があります。 - 割り当て
e1 = e2
e2
のセーフ コンテキストは、少なくともe1
のセーフ コンテキストと同じ幅のコンテキストである必要があります。
メソッドの呼び出しで、ref struct
型のref
またはout
引数 (型がreadonly
でない限り、レシーバーを含む) がセーフ コンテキストS1
の場合、引数 (レシーバーを含む) はS1
よりも狭いセーフ コンテキストを持つ可能性があります。
16.4.12.2 パラメーターセーフコンテキスト
ref 構造体型のパラメーター (インスタンス メソッドの this
パラメーターを含む) には、呼び出し元コンテキストのセーフ コンテキストがあります。
16.4.12.3 ローカル変数のセーフ コンテキスト
ref 構造体型のローカル変数には、次のようなセーフ コンテキストがあります。
- 変数が
foreach
ループの反復変数である場合、変数のセーフ コンテキストは、foreach
ループの式のセーフ コンテキストと同じです。 - それ以外の場合、変数の宣言に初期化子がある場合、変数のセーフ コンテキストは、その初期化子のセーフ コンテキストと同じです。
- それ以外の場合、変数は宣言の時点で初期化されておらず、呼び出し元コンテキストのセーフ コンテキストを持ちます。
16.4.12.4 フィールド セーフ コンテキスト
F
の型が ref 構造体型であるフィールド e.F
への参照には、e
のセーフ コンテキストと同じセーフ コンテキストがあります。
16.4.12.5 演算子
ユーザー定義演算子のアプリケーションは、メソッド呼び出しとして扱われます (§16.4.12.6)。
e1 + e2
やc ? e1 : e2
などの値を生成する演算子の場合、結果のセーフ コンテキストは、演算子のオペランドのセーフ コンテキストの中で最も狭いコンテキストです。 その結果、 +e
などの値を生成する単項演算子の場合、結果のセーフ コンテキストはオペランドのセーフ コンテキストになります。
注: 条件演算子の最初のオペランドは
bool
であるため、そのセーフ コンテキストは呼び出し元コンテキストです。 その後、結果として得られるセーフ コンテキストは、2 番目と 3 番目のオペランドの最も狭いセーフ コンテキストになります。 end note
16.4.12.6 メソッドとプロパティの呼び出し
メソッド呼び出し e1.M(e2, ...)
またはプロパティ呼び出し e.P
の結果の値には、次のコンテキストのうち最も小さいセーフ コンテキストがあります。
- 呼び出し元コンテキスト。
- すべての引数式 (受信側を含む) のセーフ コンテキスト。
プロパティの呼び出し ( get
または set
) は、上記の規則によって基になるメソッドのメソッド呼び出しとして扱われます。
16.4.12.7 stackalloc
stackalloc 式の結果には、関数メンバーのセーフ コンテキストがあります。
16.4.12.8 コンストラクターの呼び出し
コンストラクターを呼び出す new
式は、構築される型を返すと見なされるメソッド呼び出しと同じ規則に従います。
さらに、任意の初期化子が存在する場合、セーフ コンテキストは、すべてのオブジェクト初期化子式のすべての引数とオペランドのセーフ コンテキストの中で最も小さい値です。
注: これらの規則は、次の形式のコンストラクターを持たない
Span<T>
に依存します。public Span<T>(ref T p)
このようなコンストラクターにより、
Span<T>
のインスタンスがフィールドとして使用され、ref
フィールドと区別できなくなります。 このドキュメントで説明する安全規則は、C# または .NET で有効なコンストラクトではないref
フィールドによって異なります。 end note
ECMA C# draft specification