C# コンパイラエラー CS8156 について解説: 参照渡しエラーの原因と対策
CS8156は、C#で参照返しが許されない値を返す際に発生するコンパイラエラーです。
ラムダ式などで、参照可能な変数ではなく定数や計算結果を参照で返そうとするとこのエラーが起こります。
解決するには、参照可能な変数を使用するか、値返しに変更する方法があります。
エラーの概要
CS8156の基本情報
コンパイラエラー CS8156 は、参照渡しの仕様上、参照として返すことのできない式を返そうとした場合に発生します。
例えば、リテラルや計算結果など、メモリ上の変数として存在しない値を参照渡ししようとすると、このエラーが発生します。
C# の設計上、参照渡しは変数やフィールドなど実体のある値に限定されているため、その制約を超えた操作は許容されません。
エラーが発生する状況
エラー CS8156 が発生するのは、参照返しを要求するデリゲートやメソッドに、参照として扱えない式(たとえば、数値リテラルや計算結果など)を渡した場合です。
特にラムダ式で参照戻り値を指定する際、直接計算結果などを参照返しとして指定するとコンパイラーはエラーを出力します。
これにより、意図しない参照渡しが行われるのを防ぐ仕組みとして働いています。
原因の詳細解説
参照渡し仕様の制限
C# では、参照渡し(ref 戻り値)の仕様により、返す値は実際に変数などメモリ上に存在する実体でなければならない仕様になっています。
変数以外の式、たとえばリテラルや計算結果は一時的な値でしかなく、アドレスを取得することができません。
そのため、参照として返すことを想定していない値を返そうとすると、仕様上の整合性が保てずにエラーが発生してしまいます。
リテラルおよび計算結果返しの問題点
リテラルや計算結果は、その場で生成される一時的な値であり、メモリ上に安定した実体として確保されるわけではありません。
たとえば、2 + 2
の結果は計算後すぐに破棄される値です。
このような値に対して参照渡しを試みると、参照としての意味が成立しないため CS8156 エラーが発生します。
つまり、参照返しを必要とする状況では、値が格納された変数などを使用する必要があります。
コード例を使った解説
エラー発生例のコード
以下は、CS8156 エラーが発生するサンプルコードです。
コード内で、ラムダ式によってリテラルの計算結果を参照として返そうとしています。
using System;
class Test
{
// ref int を返すデリゲートの宣言
delegate ref int D1();
// エラーが発生するメソッド
static void Test1()
{
// ラムダ式内で計算結果を参照返ししようとしているためエラーが出ます
D1 d1 = () => ref (2 + 2);
}
static void Main()
{
Test1();
}
}
error CS8156: A ref or out argument must be an assignable variable
コード例のポイント解説
このコードでは、ラムダ式内で 2 + 2
という計算結果を ref
キーワードと共に返そうとしています。
計算結果は一時的な値であり、実体のある変数ではないため、参照として返すことができずエラー CS8156 が発生します。
修正例のコード
以下は、エラーを解消した修正例です。
修正例では、参照返しから通常の値返しへ変更することでエラーが解消されます。
using System;
class Test
{
// int を返すデリゲートに変更
delegate int D1();
// エラーが解消されたメソッド
static void Test1()
{
// ラムダ式で通常の値返しを行っています
D1 d1 = () => 2 + 2;
Console.WriteLine(d1());
}
static void Main()
{
Test1();
}
}
4
修正ポイントの解説
修正例では、デリゲートの返り値の型を ref int
から int
に変更し、ラムダ式で単純に計算結果を返すようにしています。
参照返しが必要ない場合、値として返すことでコンパイラーは正しく動作します。
エラー対策と改善方法
参照可能な変数の利用方法
もし参照渡しが必要な場合は、リテラルや計算結果ではなく、メモリ上に明確に存在する変数を利用する方法があります。
たとえば、次のコード例では静的変数 value
を参照渡しできる状態にし、ラムダ式内でその参照を正しく返しています。
using System;
class Test
{
// ref int を返すデリゲートの宣言
delegate ref int D1();
// 参照可能な変数の定義
static int value = 10;
// 変数の参照を返すメソッド
static ref int GetReference()
{
return ref value;
}
// 正しい参照渡しの例
static void Test1()
{
D1 d = () => ref value; // 変数 'value' はメモリ上に明確に存在するため参照として返せます
Console.WriteLine(d());
}
static void Main()
{
Test1();
}
}
10
値返しへの変更手法
参照渡しが必須ではない場合、値として返すことでエラーを回避することが可能です。
値返しに変更する際は、デリゲートやメソッドの戻り値の型を int
など、適切な型に変更し、ラムダ式内でも単純な計算結果を返すように変更します。
先ほどの修正例コードでも示したように、値返しにすることで参照渡しの問題を回避し、コンパイラーエラーを解消できます。
まとめ
この記事では、C# コンパイラエラー CS8156 の基本的な概要と、参照渡し仕様の制限によりリテラルや計算結果を参照として返せない問題について解説しています。
エラー発生の状況や、サンプルコードを用いた原因と解決方法、変数を用いた正しい参照返しや値返しへの変更手法が理解できます。
これにより、参照渡しエラーを回避するための具体的な対策が分かります。