ラムダ式でキャプチャされた変数: 「foreach ループ変数」の謎を解き明かす
リストを作成することを目的とした以下の複雑なコードを考えてみましょう。さまざまなデータ型を認識するメソッドの例:
public class MyClass { public delegate string PrintHelloType(string greeting); public void Execute() { Type[] types = new Type[]{typeof(string), typeof(float), typeof(int)}; List<PrintHelloType> helloMethods = new List<PrintHelloType>(); foreach (var type in types) { var sayHello = new PrintHelloType(greeting => SayGreetingToType(type, greeting)); helloMethods.Add(sayHello); } foreach (var helloMethod in helloMethods) { Console.WriteLine(helloMethod("Hi")); } } public string SayGreetingToType(Type type, string greetingText) { return greetingText + " " + type.Name; } }
ただし、このコードを実行すると、予期しない結果が発生します。意図した "Hi String"、"Hi" の代わりに、"Hi Int32" が 3 回出力されます。シングル」、「Hi Int32」。ここで何が起こっているのでしょうか?
クロージャとキャプチャされた変数の微妙さ
この動作を理解する鍵は、クロージャとキャプチャされた変数の概念にあります。ラムダ式が作成されると、周囲のスコープから変数がキャプチャされます。この場合、ラムダ式は foreach ループから型変数をキャプチャします。
キャプチャされたループ変数: 落とし穴
重要なことに、キャプチャされた変数は次の値ではありません。型ではなく、変数そのものです。ループが進行するにつれて、型変数が変更されますが、ラムダ式は引き続き同じキャプチャされた変数を参照します。
問題の修正
意図した動作を実現するには、意図した値を取得するには、ループ内に新しい変数を作成する必要があります。コードを変更する方法は次のとおりです。
foreach (var type in types) { var newType = type; var sayHello = new PrintHelloType(greeting => SayGreetingToType(newType, greeting)); helloMethods.Add(sayHello); }
型の代わりに newType をキャプチャすることにより、ラムダ式は意図した値への定数参照を持つようになり、期待される出力が取得されます。
結論
ラムダ式を操作する場合、クロージャとキャプチャされた変数の複雑さを理解することが不可欠です。これらの仕組みに留意することで、予期しない動作を回避し、コードが意図したとおりに動作するようにすることができます。
以上がforeach ループ内のすべてのラムダ式が、個々の値ではなく同じ変数をキャプチャするのはなぜですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。