参照安全に関するエラーと警告

参照変数の安全規則に違反すると、次のエラーが発生する可能性があります。

  • CS8166: ref パラメーターではないため、そのパラメーターを参照渡しで返すことができません
  • CS8167: そのパラメーターが ref パラメーターでも out パラメーターでもないため、そのメンバーを参照渡しで返すことはできません
  • CS8168: ref ローカルではないため、ローカル変数を参照で返すことはできません
  • CS8169: ref ローカルではないため、そのローカル変数のメンバーを参照渡しで返すことができません
  • CS8345: フィールドまたは自動実装プロパティは、 ref structのインスタンス メンバーでない限り、型にすることはできません。
  • CS8348: 宣言スコープの外部でパラメーター 'parameter name' によって参照される変数を公開する可能性があるため、このコンテキストでは 'member' の結果のメンバーを使用できません
  • CS8349: 式は宣言スコープの外部で間接的に変数を公開する可能性があるため、このコンテキストでは使用できません
  • CS8350: この 'member' への引数の組み合わせは禁止されています。これは、パラメーター 'parameter name' によって参照される変数が宣言スコープの外部で公開される可能性があるためです。
  • CS8351: ref 条件付きの演算子のブランチでは、互換性のない宣言スコープを持つ変数を参照できません
  • CS8374: ref 代入できません。ソースのエスケープ スコープが宛先よりも狭くなっています。
  • CS9075: 現在のメソッドにスコープが設定されているため、参照によってパラメーターを返すことができません
  • CS9076: パラメーターのメンバーは現在のメソッドにスコープ設定されているため、参照によって返すことができません
  • CS9077: ref パラメーターを介してパラメーターを参照渡しで返すことができません。それは return ステートメントでのみ返すことができます
  • CS9078: ref パラメーター経由で引数のメンバーを参照渡しで返すことはできません。return ステートメントでのみ返すことができます
  • CS9079: ソースは return ステートメントを介してのみ現在のメソッドをエスケープできるため、ソースを変換先に ref-assign できません。
  • CS9096: ソースの値のエスケープ スコープが、宛先よりも広いため、ソースを宛先に ref 割り当てすることはできません。これは、宛先を通じて、ソースよりも狭いエスケープ スコープを持つ値を割り当てることが可能になるためです。

参照変数の安全規則に違反すると、次の警告が発生する可能性があります。

  • CS9080: このコンテキストで変数を使用すると、参照先の変数が宣言スコープの外部に公開される場合があります
  • CS9081: このコンテキスト内の型の stackalloc 式の結果は、包含メソッドの外部で公開される可能性があります
  • CS9082: Local は参照によって返されますが、参照で返すことができない値に初期化されました
  • CS9083: メンバーは参照によって返されますが、参照で返すことができない値に初期化されました
  • CS9084: 構造体メンバーは、参照によって 'this' または他のインスタンス メンバーを返します
  • CS9085: ソースを宛先に割り当てますが、ソースのエスケープ スコープは宛先よりも狭くなります。
  • CS9086: ref 条件演算子の分岐は、互換性のない宣言スコープを持つ変数を参照します
  • CS9087: これはパラメーターを参照返しで返しますが、ref パラメーターではありません
  • CS9088: これは参照によってパラメーターを返しますが、現在のメソッドにスコープが設定されています
  • CS9089: これは、ref パラメーターでも out パラメーターでもないパラメーターのメンバーを参照渡しで返します
  • CS9090: これは、現在のメソッドをスコープとするパラメーターのメンバーを参照して返します
  • CS9091: ローカル変数を参照で返していますが、ref ローカルではありません
  • CS9092: これはローカルのメンバーを参照渡しで返しますが、ref ローカルではありません
  • CS9093: この ref はソースを宛先に割り当てますが、ソースは return ステートメントを介してのみ現在のメソッドをエスケープできます。
  • CS9094: これは ref パラメーターを介してパラメーターを参照渡しで返しますが、それは return ステートメントでのみ安全に返すことができます
  • CS9095: これは、ref パラメーターを介してパラメーターのメンバーを参照渡しで返しますが、それは return ステートメントでのみ安全に返すことができます
  • CS9097: この ref はソースを宛先に割り当てますが、ソースは宛先よりも広い値のエスケープ スコープを持ち、ソースよりも狭いエスケープ スコープを持つ値の宛先を介して割り当てることができます。

互換性のないスコープを持つ参照を返す

コンパイラは、変数の有効期間がメソッドのスコープを超えない場合に、変数への参照を返さないようにします。 これらのエラーは、 ref で宣言しなかったパラメーター、ローカル変数、またはメンバーを参照して返そうとしたとき、または現在のメソッドにスコープが制限されている場合に発生します。

エラー:

  • CS8166: ref パラメーターではないため、そのパラメーターを参照渡しで返すことができません
  • CS8167:パラメーターが ref パラメーターでも out パラメーターでもないため、そのメンバーを参照によって返すことはできません
  • CS8168: ref ローカルではないため、そのローカル変数を参照で返すことはできません
  • CS8169: ref ローカルではないため、そのローカル変数のメンバーを参照渡しで返すことができません
  • CS9075: 現在のメソッドにスコープが設定されているため、参照によってパラメーターを返すことができません
  • CS9076: パラメーターのメンバーは現在のメソッドにスコープ設定されているため、参照によって返すことができません
  • CS9077: ref パラメーターを介してパラメーターを参照渡しで返すことはできません。パラメーターは return ステートメント でのみ返すことができます
  • CS9078: ref パラメーターを介して、パラメーターのメンバーを参照として返すことはできません。返せるのは return ステートメント内のみです

警告:

  • CS9087: これはパラメーターを参照返しで返しますが、ref パラメーターではありません
  • CS9088: これは参照によってパラメーターを返しますが、現在のメソッドにスコープが設定されています
  • CS9089: これは、ref パラメーターでも out パラメーターでもないパラメーターのメンバーを参照渡しで返します
  • CS9090: これは、現在のメソッドをスコープとするパラメーターのメンバーを参照して返します
  • CS9091: これはローカルを参照渡しで返しますが、ref ローカルではありません
  • CS9092: これはローカル変数のメンバーを参照として返しますが、ref ローカルではありません
  • CS9094: これは ref パラメーターを介してパラメーターを参照渡しで返しますが、それは return ステートメントでのみ安全に返すことができます
  • CS9095: これは、ref パラメーターを介してパラメーターのメンバーを参照渡しで返しますが、それは return ステートメントでのみ安全に返すことができます

これらのエラーを解決するには:

  • メソッド シグネチャを変更して、パラメーターを値渡しではなく、 ref キーワードで宣言します。 この変更により、呼び出し元が変数の有効期間 (CS8166、CS8167、CS9087、CS9089) を制御するため、パラメーターの保存場所を安全に返すことができます。
  • ローカル変数の場合は、ref を返す式または ref パラメーターから割り当てることで、ローカル変数を ref ローカルとして宣言します。 この変更により、ローカルは、メソッド スコープの有効期間 (CS8168、CS8169、CS9091、CS9092) を持つ新しい変数を作成するのではなく、十分な有効期間を持つストレージを参照します。
  • scoped修飾子を使用してパラメーターを宣言する場合は、参照によって返されないようにします。 scoped修飾子は、パラメーターの参照がメソッドをエスケープしないように明示的に制限し、潜在的な未解決参照 (CS9075、CS9076、CS9088、CS9090) を防ぎます。
  • ref パラメーターから参照を返す必要がある場合は、別のref パラメーターに参照を割り当てて、その参照を返すのではなく、直接のreturn ref ステートメントを使用します。 コンパイラは、直接戻りステートメント (CS9077、CS9078、CS9094、CS9095) でのみエスケープ スコープを追跡できます。

ref 安全規則の詳細については、 ref 戻り値 に関する記事と ref safe コンテキストの C# 標準セクションを参照してください。

互換性のないスコープを持つ Ref の割り当て

コンパイラは、ソース変数のエスケープ スコープが宛先よりも狭い ref 代入操作を防ぎます。 参照の割り当てを行うと、宛先からソースの格納場所への参照が作成されます。 ソースがターゲットの前にスコープ外になった場合、宛先は無効なメモリを参照します。

エラー:

  • CS8374: ref 代入できません。ソースのエスケープ スコープが宛先よりも狭くなっています。
  • CS9079: ソースは return ステートメントを介してのみ現在のメソッドをエスケープできるため、ソースを宛先に ref 代入できません。
  • CS9096: 宛先よりも広い値のエスケープ スコープがソースにあり、ソースよりも狭いエスケープ スコープを持つ値の宛先を介して代入が可能であるため、ソースを宛先に ref 代入できません。

警告:

  • CS9085: これはソースを宛先に ref 代入しますが、ソースのエスケープ スコープは宛先よりも狭くなっています。
  • CS9093: これはソースを宛先に ref 代入しますが、ソースは return ステートメントを介してのみ現在のメソッドをエスケープできます。
  • CS9097: これはソースを宛先に ref 代入しますが、ソースの値のエスケープ スコープの方が宛先より広いため、ソースより狭いエスケープ スコープを持つ値を宛先を通じて代入できてしまいます。

これらのエラーを解決するには:

  • ref 代入のソース変数に、少なくともコピー先変数と同じ幅のエスケープ スコープが含まれるように、コードを再構築します。 この変更により、宛先参照が有効期間全体にわたって有効であり、未解決の参照 (CS8374、 CS9085) が防止されます。
  • 変数が return ステートメントを介してのみメソッドをエスケープできる場合は、他の方法でアクセス ref 変数に割り当てないでください。 たとえば、フィールドに格納したり、ref パラメーターを使用して返したりします。 これらのアクションは、ソースを return ステートメント (CS9079、CS9093) でのみ使用できるという制限 に違反します。
  • 値エスケープ スコープを含む ref 代入の場合は、ソースの値エスケープ スコープが宛先よりも広くないことを確認します。 不一致により、宛先参照を通じて範囲が狭い値を割り当て、有効期間の短い値 (CS9096、 CS9097) への参照が作成される可能性があります。

ref 安全規則の詳細については、 ref 戻り値 に関する記事と ref safe コンテキストの C# 標準セクションを参照してください。

スコープ違反の回避と条件演算子

コンパイラは、変数がさまざまな操作を通じて宣言スコープをエスケープする方法を追跡します。 これらのエラーは、条件演算子やstackalloc式など、有効な有効期間外に参照される変数を公開する可能性があるコンテキストで変数ref使用すると発生します。

エラー:

  • CS8348: 宣言スコープの外部でパラメーター 'parameter name' によって参照される変数を公開する可能性があるため、このコンテキストでは 'member' の結果のメンバーを使用できません
  • CS8349: 式は宣言スコープの外部で間接的に変数を公開する可能性があるため、このコンテキストでは使用できません
  • CS8350: この 'member' への引数の組み合わせは禁止されています。これは、パラメーター 'parameter name' によって参照される変数が宣言スコープの外部で公開される可能性があるためです。
  • CS8351: ref 条件付きの演算子のブランチでは、互換性のない宣言スコープを持つ変数を参照できません

警告:

  • CS9080: このコンテキストで変数を使用すると、参照先の変数が宣言スコープの外部に公開される場合があります
  • CS9081: このコンテキスト内の型の stackalloc 式の結果は、包含メソッドの外部で公開される可能性があります
  • CS9086: ref 条件付きの演算子のブランチでは、互換性のない宣言スコープを持つ変数を参照します

これらのエラーを解決するには:

  • 呼び出しの結果または参照を保持し得る別の式に対するメンバーアクセスでは、それらの参照の有効期間が派生元のパラメーターまたはローカル変数の有効期間を超える可能性があるコンテキストで、その値を使用しないでください。 API コントラクトに応じて、安全なスコープ内で使用を維持し、十分に長い有効期間でストレージを渡すか、メソッドが参照をエスケープできない場合にパラメーターに scoped を追加するなど、API シグネチャを変更します (CS8348、 CS8349)。
  • 呼び出し引数の組み合わせが禁止されている場合は、より狭いセーフ スコープの引数を別の引数またはより広いスコープの受信者に格納できないようにします。 API コントラクトに応じて、互換性のある有効期間を持つ引数を使用するか、操作を分離するか、参照をエスケープできないパラメーターに適切な scoped 修飾子を追加します (CS8350)。
  • true 分岐と false 分岐の両方が互換性のある宣言スコープを持つ変数を参照するように、ref条件演算子 (ref 戻り値を持つ?:演算子) を変更します。 両方の変数に、少なくとも同じスコープ レベルまで拡張される有効期間が必要です。これにより、条件式が無効になる参照が返される可能性がありません (CS8351、 CS9086)。
  • 式またはメソッド呼び出しで変数を使用する場合は、コンテキストで参照される変数が宣言スコープを超えてエスケープできないようにします。 通常、スコープ付き変数は、意図した有効期間 (CS9080) を超えてキャプチャまたは格納される可能性があるメソッドまたは式に渡さないようにします。
  • stackalloc式の場合は、結果を変数に割り当てたり、スタック割り当てメモリが包含メソッドの外部でアクセスできるコンテキストで使用したりしないでください。 メソッドは、返されるときにスタック割り当てメモリを自動的に解放し、その後アクセスすると未定義の動作になります (CS9081)。

詳細については、 ref 戻り値に関する記事、 メモリ使用量に関する記事、および ref セーフ コンテキストに関する C# 標準セクションを参照してください。

構造体のメンバーとフィールドの制約

コンパイラは、構造体のメンバーとフィールドに対して特殊な規則を適用して、未解決の参照を防ぎます。 これらのエラーは、構造体メンバーがインスタンス状態への参照を返す場合、またはフィールドに特別な処理を必要とする型がある場合に発生します。

エラー:

  • CS8345: フィールドまたは自動実装プロパティは、それが ref struct のインスタンス メンバーである場合を除いて、型にすることができません。

警告:

  • CS9082: Local は参照によって返されますが、参照で返すことができない値に初期化されました
  • CS9083: メンバーは参照によって返されますが、参照で返すことができない値に初期化されました
  • CS9084: 構造体メンバーは、参照によって 'this' または他のインスタンス メンバーを返します

これらのエラーを解決するには:

  • ref に似た型 ( Span<T> 型や ref struct 型など) を持つフィールドと自動実装プロパティは、通常の構造体やクラスではなく、ref 構造体内のインスタンス メンバーとしてのみ宣言されるようにします。ref に似た型はスタック上にのみ安全に存在でき、ref 構造体は必要な有効期間保証 (CS8345) を提供するためです。
  • メソッドから参照によってローカル変数を返す場合は、有効期間の短いストレージへの参照を作成する値型式またはローカル スコープ変数 (CS9082、 CS9083) ではなく、ref パラメーターや ref を返すメソッド呼び出しなどの十分なエスケープ スコープを持つソースからローカルが初期化されたことを確認します。
  • 構造体インスタンス のメソッドまたはプロパティでは、参照によって this またはインスタンス フィールドを返さないようにします。これは、構造体は頻繁にコピーされる値型であり、インスタンス メンバーへの参照を返すことで、メソッドが戻った後に破棄される一時的なコピーへの参照が作成される可能性があるためです (CS9084)。

詳細については、 ref 構造体型 に関する記事と、 ref セーフ コンテキストに関する C# 標準セクションを参照してください。