[解決済み] クロージャでのforeach変数へのアクセスに関する警告
質問
以下のような警告が表示されます。
クロージャ内のforeach変数へのアクセスです。コンパイラのバージョンが異なると動作が異なる可能性があります。
エディタで見るとこんな感じです。
この警告を修正する方法は知っていますが、なぜこの警告が出るのか知りたいのです。
CLRのバージョンに関するものですか?ILと関係があるのでしょうか?
どのように解決するのですか?
この警告には2つの部分があります。1つ目は...
クロージャ内のforeach変数へのアクセス
...これはそれ自体無効ではありませんが、一見すると直感に反しています。また、正しく実行するのは非常に困難です。(以下にリンクする記事では、これを "有害" と表現しているほどです)。
あなたが抜粋したコードは、基本的に C# コンパイラ (C# 5 以前) が生成したものを拡張したものであることに注意して、あなたのクエリを使ってみましょう。
foreach
1
:
なぜ[以下が]有効でないのかが[わからない]のです。
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
(の値を表示するという、まったく同じことをする5つの関数を作成したからです。) 実際には、これらは同じ関数です("Print
s
"、"プリント
s
"、"プリント
s
"・・・)。
それらを使おうとする時点で、それらはまさに私たちが要求することを行います。
s
. の最後の既知の値を見てみましょう。
s
を見ると、それが
5
. というわけで、次のようになります。
s == 5
はコンソールに5回出力されます。
これはまさに私たちが求めたものですが、おそらく私たちが望むものではありません。
警告の第二弾は...
異なるバージョンのコンパイラでコンパイルした場合、異なる挙動を示すことがあります。
...は、その通りです。
C# 5 からは、コンパイラが別のコードを生成して、"prevent" を経由してこれが発生しないようになりました。
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"で起こるそうです。そのため
おそらく、.NET 4.5 以降をターゲットにする必要があります。
をターゲットとし、C# 5 以降のコンパイラーを使用しなければ新しい動作は得られず、それ以前のものはすべて古い動作になります。
しかし、明確には、これはコンパイラーの機能であり、.NET Framework のバージョンではありません。
ILとの関連性はあるのでしょうか?
コードが違えば生成されるILも違うので、その意味では生成されるILに結果があります。
1
foreach
は、あなたがコメントで投稿したコードよりもはるかに一般的な構成です。この問題は、通常
foreach
を使用することで発生するもので、手動で列挙することで発生するものではありません。そのため
foreach
の変更はこの問題を防ぐのに役立ちますが、完全ではありません。
関連
-
[解決済み】「The breakpoint will not currently be hit」を改善するには?このドキュメントにはシンボルが読み込まれていません。" という警告はどうすれば改善されますか?
-
[解決済み】ASP.NET Core Dependency Injectionのエラーです。アクティブ化しようとしているときに、タイプのサービスを解決できません。
-
[解決済み】取り消せないメンバはメソッドのように使えない?
-
[解決済み] 'IEnumerable<SelectListItem>' 型の ViewData アイテムで、キーが国であるものは存在しない。
-
[解決済み】2つ(またはそれ以上)のリストを1つに統合する(C# .NETで
-
[解決済み】IntPtrとは一体何なのか?
-
[解決済み】プロセスが実行されているかどうかを知るには?
-
[解決済み] C#がforeachで変数を再利用するのは理由があるのか?
-
[解決済み] コンストラクタ内の仮想メンバー呼び出し
-
[解決済み] エラー - IISメタベースにアクセスできません。
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] [Solved] 1つ以上のエンティティで検証に失敗しました。詳細は'EntityValidationErrors'プロパティを参照してください [重複]。
-
[解決済み】プログラム実行中に1秒待つ
-
[解決済み】「入力文字列が正しい形式ではありませんでした」エラーの解決方法は?[重複しています]。
-
[解決済み】非静的メソッドはターゲットを必要とする
-
[解決済み】ORA-01008: すべての変数がバインドされていません。これらはバインドされています。
-
[解決済み】「namespace」なのに「type」のように使われる。
-
[解決済み】ファイルやアセンブリ、またはその依存関係の1つをロードできませんでした。
-
[解決済み】Microsoft.Extensions.LoggingからILoggerを解決することができない
-
[解決済み】別のスレッドがこのオブジェクトを所有しているため、呼び出し側のスレッドはこのオブジェクトにアクセスできない
-
[解決済み] C#がforeachで変数を再利用するのは理由があるのか?