[解決済み] モディファイドクロージャーへのアクセス(2)
質問
の質問を拡張したものです。 変更されたクロージャへのアクセス . 私はちょうど次のものが実際に生産的な使用のために十分に安全であるかどうかを確認したいです。
List<string> lists = new List<string>();
//Code to retrieve lists from DB
foreach (string list in lists)
{
Button btn = new Button();
btn.Click += new EventHandler(delegate { MessageBox.Show(list); });
}
起動時に1回だけ上記を実行しています。今のところ、問題なく動作しているようです。Jonが言ったように、場合によっては直感に反する結果になることがあります。ここで気をつけるべきことは何でしょうか?リストが複数回実行されても大丈夫なのでしょうか?
どのように解決するのですか?
C# 5 より前のバージョンでは、変数を再宣言する必要があります。 の内部 さもなければ、変数が共有され、すべてのハンドラが最後の文字列を使用することになります。
foreach (string list in lists)
{
string tmp = list;
Button btn = new Button();
btn.Click += new EventHandler(delegate { MessageBox.Show(tmp); });
}
重要なのは、C# 5以降、これが変更されたことに注意してください。
の場合、特に
foreach
の場合は、もうこれを行う必要はありません:質問のコードは期待通りに動作します。
この変更なしでは動作しないことを示すために、以下を考えてみましょう。
string[] names = { "Fred", "Barney", "Betty", "Wilma" };
using (Form form = new Form())
{
foreach (string name in names)
{
Button btn = new Button();
btn.Text = name;
btn.Click += delegate
{
MessageBox.Show(form, name);
};
btn.Dock = DockStyle.Top;
form.Controls.Add(btn);
}
Application.Run(form);
}
上記を実行する をC# 5より先に実行する。 を実行すると、各ボタンは異なる名前を表示しますが、ボタンをクリックすると "Wilma"が4回表示されます。
これは、言語仕様 (ECMA 334 v4, 15.8.4) (C# 5 より前) で定義されているためです。
foreach (V v in x)
embedded-statement
が展開されます。{ E e = ((C)(x)).GetEnumerator(); try { V v; while (e.MoveNext()) { v = (V)(T)e.Current; embedded-statement } } finally { … // Dispose e } }
なお、変数
v
(これはあなたの
list
が宣言されています。
の外側
の外側で宣言されています。したがって、キャプチャされた変数の規則によって、リストのすべての反復はキャプチャされた変数ホルダーを共有することになります。
C# 5以降では、これが変更されます。反復変数(
v
) がスコープされ
の内側
というループになります。仕様の参考資料がないのですが、基本的にはこうなります。
{
E e = ((C)(x)).GetEnumerator();
try {
while (e.MoveNext()) {
V v = (V)(T)e.Current;
embedded-statement
}
}
finally {
… // Dispose e
}
}
Re unsubscribe; anonymous handlerを積極的にunsubscribeしたい場合、ハンドラ自身をキャプチャするのがコツです。
EventHandler foo = delegate {...code...};
obj.SomeEvent += foo;
...
obj.SomeEvent -= foo;
同様に、一回限りのイベントハンドラ(Loadなど)が必要な場合。
EventHandler bar = null; // necessary for "definite assignment"
bar = delegate {
// ... code
obj.SomeEvent -= bar;
};
obj.SomeEvent += bar;
これは現在、自己購読を解除しています ;-p
関連
-
[解決済み】コレクションが変更され、列挙操作が実行されないことがある。
-
[解決済み】なぜこのコードはInvalidOperationExceptionを投げるのですか?
-
[解決済み] モディファイドクロージャーへのアクセス
-
[解決済み] ループ内のJavaScriptクロージャ - シンプルな実用例
-
[解決済み] クロージャ」と「ラムダ」の違いは何ですか?
-
[解決済み] エラー - IISメタベースにアクセスできません。
-
[解決済み] C#では、public、private、protected、アクセス修飾子がないことの違いは何ですか?
-
[解決済み] C# "internal "アクセス修飾子でユニットテストを行う場合
-
[解決済み] PHPでは、クロージャとは何ですか?なぜ "use "識別子を使用するのですか?
-
[解決済み】ReSharperが「暗黙のうちに捕捉されたクロージャ」と言うのはなぜ?
最新
-
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 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】エラー。「戻り値を変更できません」 C#
-
[解決済み】C# ASP.NET使用時に「WebClientのリクエスト中に例外が発生しました。
-
[解決済み】クロススレッド操作が有効でない。作成されたスレッド以外のスレッドからアクセスされたコントロール
-
[解決済み】ORA-01008: すべての変数がバインドされていません。これらはバインドされています。
-
[解決済み】ランダムなブーリアンを生成する最速の方法
-
[解決済み] ...基礎となる接続は閉じられました。予期しないエラーが受信で発生しました
-
[解決済み】インデックスが範囲外でした。コレクションパラメータname:indexのサイズより小さく、非負でなければなりません。
-
[解決済み】IntPtrとは一体何なのか?
-
[解決済み】プロセスが実行されているかどうかを知るには?
-
[解決済み] モディファイドクロージャーへのアクセス