次の警告が表示されます。
クロージャ内のforeach変数へのアクセス。異なるバージョンのコンパイラでコンパイルすると、動作が異なる場合があります。
これは私のエディターでどのように見えるかです:

この警告を修正する方法は知っていますが、なぜこの警告が表示されるのか知りたいのですが。
これは「CLR」バージョンについてですか?「IL」と関係がありますか?
次の警告が表示されます。
クロージャ内のforeach変数へのアクセス。異なるバージョンのコンパイラでコンパイルすると、動作が異なる場合があります。
これは私のエディターでどのように見えるかです:

この警告を修正する方法は知っていますが、なぜこの警告が表示されるのか知りたいのですが。
これは「CLR」バージョンについてですか?「IL」と関係がありますか?
回答:
この警告には2つの部分があります。最初は...
クロージャ内のforeach変数へのアクセス
...それ自体は無効ではありませんが、一見すると直感に反します。また、正しく行うことは非常に困難です。(以下にリンクする記事では、これを「有害」と説明しているほどです。)
クエリを実行します。抜粋したコードは、基本的に、C#コンパイラ(C#5より前)がforeach1に対して生成するものの拡張形式であることに注意してください。
なぜ[以下が]無効なのか[わかりません]:
string s; while (enumerator.MoveNext()) { s = enumerator.Current; ...
まあ、それは構文的に有効です。そして、ループで行っているのがの値を使用しているだけであればs、すべてが良好です。しかし、閉鎖するsと、直感に反する行動につながります。次のコードを見てください。
var countingActions = new List<Action>();
var numbers = from n in Enumerable.Range(1, 5)
select n.ToString(CultureInfo.InvariantCulture);
using (var enumerator = numbers.GetEnumerator())
{
string s;
while (enumerator.MoveNext())
{
s = enumerator.Current;
Console.WriteLine("Creating an action where s == {0}", s);
Action action = () => Console.WriteLine("s == {0}", s);
countingActions.Add(action);
}
}
このコードを実行すると、次のコンソール出力が得られます。
Creating an action where s == 1
Creating an action where s == 2
Creating an action where s == 3
Creating an action where s == 4
Creating an action where s == 5
これはあなたが期待することです。
おそらく予期しないものを表示するには、上記のコードの直後に次のコードを実行します。
foreach (var action in countingActions)
action();
次のコンソール出力が表示されます。
s == 5
s == 5
s == 5
s == 5
s == 5
どうして?すべてがまったく同じことを行う5つの関数を作成したため、s(閉じた)の値を出力します。実際には、これらは同じ機能です(「印刷s」、「印刷s」、「印刷s」...)。
それらを使用する時点で、それらは私たちが求めることを正確に実行しますs。の値を出力します。の最後の既知の値をs見ると、それがであることがわかります5。したがってs == 5、コンソールに5回印刷されます。
これはまさに私たちが求めていたものですが、おそらく私たちが望んでいるものではありません。
警告の2番目の部分...
異なるバージョンのコンパイラでコンパイルすると、動作が異なる場合があります。
...それが何であるかです。C#5以降、コンパイラは、を介してこれが発生するのを「防止」するさまざまなコードを生成しforeachます。
したがって、次のコードは、コンパイラのバージョンが異なると、異なる結果を生成します。
foreach (var n in numbers)
{
Action action = () => Console.WriteLine("n == {0}", n);
countingActions.Add(action);
}
その結果、R#警告も生成されます:)
上記の最初のコードスニペットは、使用していないため、コンパイラのすべてのバージョンで同じ動作を示しますforeach(むしろ、C#5以前のコンパイラと同じように拡張しました)。
これはCLRバージョン用ですか?
ここで何を求めているのかよくわかりません。
Eric Lippertの投稿によると、変更は「C#5で」行われます。そうおそらく、.NET4.5以降をターゲットにする必要があります C#5以降のコンパイラを使用して新しい動作を取得し、それ以前のすべてが古い動作を取得します。
ただし、明確にするために、これはコンパイラの機能であり、.NETFrameworkのバージョンではありません。
ILとの関連性はありますか?
コードが異なればILも異なるため、その意味で生成されたILに影響があります。
1 foreachは、コメントに投稿したコードよりもはるかに一般的な構成です。この問題は通常foreach、手動の列挙ではなく、の使用によって発生します。そのためforeach、C#5での変更はこの問題の防止に役立ちますが、完全ではありません。
foreachものは質問の内容から来ています。あなたはそれがさまざまな、より一般的な方法で起こり得るということは正しいです。
最初の答えは素晴らしいので、1つだけ追加したいと思いました。
サンプルコードでは、reflectedModelにIEnumerableが割り当てられているため、警告が表示されます。これは、列挙時にのみ評価されます。より広いスコープを持つものにreflectedModelを割り当てた場合、列挙自体がループの外側で発生する可能性があります。 。
変更した場合
...Where(x => x.Name == property.Value)
に
...Where(x => x.Name == property.Value).ToList()
次に、reflectedModelにはforeachループ内で明確なリストが割り当てられるため、列挙はループの外側ではなくループ内で確実に行われるため、警告は表示されません。
ブロックスコープの変数は警告を解決するはずです。
foreach (var entry in entries)
{
var en = entry;
var result = DoSomeAction(o => o.Action(en));
}