C#:キャプチャした変数が解放されないパターンについて
class Program { static void Main(string[] args) { localMethod(); MyAction(); GC.Collect(); GC.WaitForPendingFinalizers(); Console.ReadLine(); } private static void localMethod() { MyClass myClass = new MyClass(); MyAction += () => { //ラムダ式内でローカル変数myClassを参照 myClass.Method(); }; } public class MyClass { public void Method() { Console.WriteLine("Closure"); } ~MyClass() { Console.WriteLine("Destructor"); } } private static Action MyAction; }
結果:
Closure
上記処理では、localMethodメソッド内でnewしたMyClassがMyActionに参照されたため、 オブジェクトの寿命がMyActionに引きずられることでデストラクタが呼び出されません。
対策として、Main関数で下記のようにMyActionにnullを代入すればデストラクタが読みだされます。
static void Main(string[] args) { localMethod(); MyAction(); //null代入を追加 MyAction = null; GC.Collect(); GC.WaitForPendingFinalizers(); Console.ReadLine(); }
結果:
Closure
Destructor
なお、ラムダ式を直接-=演算子で代入してもメソッド登録を解除できないため、MyActionの宣言が
private static event Action MyAction;
であった場合は、別のActionオブジェクトに=でラムダ式を格納し、そのラムダ式でMyActionに+=で登録、-=で登録解除するといった工夫が必要そうです。