ReSharper 2026.1 Help

コードインスペクション: 変更されたキャプチャー変数へのアクセス

まず、 クロージャ(英語)とは何かを理解していることを確認しましょう。 簡単に言うと、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を取得します。

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

ループ内で上記のクロージャを定義する例を次に示します。

var myActions = new List<Action>(); var myStrings = new List<string>() { "one", "two", "three" }; for (var i = 0; i < myStrings.Count; i++) { Action print = () => { Console.WriteLine(myStrings[i]); }; myActions.Add(print); } myActions[0]();

驚くべきことに、このコードは ArgumentOutOfRangeException を生成します。 myActions[0](); を呼び出したときです。 ここで発生するのは次のことです。直感的に思える Console.WriteLine(myStrings[0]); の実行ではなく、この呼び出しは Console.WriteLine(myStrings[i]); を実行しようとします。さらに、 ifor サイクル全体のスコープとなっているため、その値は 0 とは異なり、さらに 2 でもありません(条件が最後に真であったときの値)。 最後の ++ 操作の結果、条件が偽となりループを抜ける直前に値は 3 となりました。 myStrings には 3 つの要素しかないため、 myStrings[3]ArgumentOutOfRangeException につながります。

ReSharper は結果(ここでは ArgumentOutOfRangeException の形)を推論しませんが、問題の原因であるクロージャ内の繰り返し変数を正しく指摘し、変更される変数の値をクロージャが定義されるスコープにコピーして修正することを提案します:

for (var i = 0; i < myStrings.Count; i++) { var i1 = i; Action print = () => { Console.WriteLine(myStrings[i1]); }; myActions.Add(print); }

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

2026 年 6 月 12 日