JetBrains Rider 2026.1 Help

コードインスペクション:クロージャ内の foreach 変数へのアクセス

まず、 クロージャ(英語)とは何かを理解していることを確認しましょう。 簡単に言うと、C# のクロージャは、ラムダ式または外部スコープからいくつかの変数をキャプチャーする匿名メソッドです。 最も簡単な例を次に示します。

// A self-contained lambda. Not a closure. Action printOne = () => { Console.WriteLine("one"); }; // A closure – a lambda that captures a variable from an outer scope. string myStr = "one"; Action print = () => { Console.WriteLine(myStr); };

上記の例では、 print は変数 myStr (その ではなく)をキャプチャーし、 print() を呼び出したときにのみ myStrを取得します。

より複雑なシナリオでは、クロージャが変化するコンテキストで定義されると、期待通りに動作しないことがあります。

発生する可能性のある状況の 1 つは、C# 4.0(Visual Studio 2010)またはそれ以前のバージョンでコンパイルされた foreach ステートメント内で定義されたクロージャです。

C# 5.0(Visual Studio 2012)の前に、コンパイラーは foreachwhile ステートメントに展開し、ループ外に定義された反復変数を使用します。 この変数がクロージャーで使用された場合、後でこのクロージャーを呼び出すと、ループの最後の反復に対応する値が常に得られます。

var myActions = new List<Action>(); var myStrings = new List<string>() { "one", "two", "three" }; foreach (var str in myStrings) { myActions.Add(() => { Console.WriteLine(str); }); } myActions[0](); // "three" is printed

JetBrains Rider はこの問題を検出し、ループ変数の値をクロージャが定義されているスコープにコピーすることで修正することを提案します:

var myActions = new List<Action>(); var myStrings = new List<string>() { "one", "two", "three" }; foreach (var str in myStrings) { var str1 = str; myActions.Add(() => { Console.WriteLine(str1); }); } myActions[0](); // "one" is printed

この修正により、 myActions からアクションを選択し、このアクションが作成されたコンテキストを取得すると、 str は対応する反復の値を保持します。

2026 年 6 月 12 日