誰も参照していないフォームは、何故 GC の対象にならないのか
渋木宏明(ひどり)さんからのお題。
モーダレスなフォームである Form1, Form2 が存在し、Form1 上に TextBox1 が配置されている時
TextBox textBox = TextBox1;
Form1.Controls.Remove (textBox);
Form2.Controls.Add (textBox);
が「ちゃんと動く」のは何故でしょうか?
特に、Remove() と Add() の行間に注目です。
Windows には「子ウィンドウ(=コモンコントロールは出来合いの子ウィンドウです)は必ずトップレベルウィンドウに保有されていなければならない」という大原則があるはずなのですが。。。
答えは「WindowsFormsParkingWindow」にある。
以下は私の完全なる憶測である(デバッガなり Spy なりを使って検証しただけで、ソースがあるわけではない)。鵜呑みにしないように。
「WindowsFormsParkingWindow」とは何なのか。
親が存在しなくなるような子ウィンドウ(今回の例では TextBox1)は存在してはいけない。そこで .NET は見えない親フォームを用意する事にした。それが「WindowsFormsParkingWindow」である。「WindowsFormsParkingWindow」は、アプリケーションドメイン単位かプロセス単位かどうかは未検証だが、とにかく一つだけ存在する。「WindowsFormsParkingWindow」は親がいない子ウィンドウを一手に引き受ける。
「WindowsFormsParkingWindow」は常に存在しているわけではない。親が確定しない子ウィンドウが出現したとき、初めて「WindowsFormsParkingWindow」も出現する。子ウィンドウの親が確定すれば「WindowsFormsParkingWindow」は消える。
Form1.Controls.Remove (textBox); --- (1)
上記が実行されたとき、Form1.Controls から textBox を削除し、textBox の親を Form1 から 「WindowsFormsParkingWindow」に変える。
さらに
Form2.Controls.Add (textBox); --- (2)
が実行されたとき、textBox の親を「WindowsFormsParkingWindow」から Form2 に変える。そして「WindowsFormsParkingWindow」に属する子ウィンドウが存在しなければ「WindowsFormsParkingWindow」は消える。
さらに面白い現象は、今 (2) が実行された直後、つまり textBox が Form2 の子ウィンドウである状態で、さらに (1) を実行したらどうなるだろうか。何と、Form2 から textBox が削除され、「WindowsFormsParkingWindow」に textBox が移動するのである。
ControlCollection.Remove( Control value ) が行っている事は、value が自身に存在すれば自身から削除し(自身に存在しなければスルー)、さらに value の親を「WindowsFormsParkingWindow」にするという事である。単にコレクションから削除しているだけではないのだ。
これらの事から、Control を単に Remove() しただけでは、GC の対象にならないという事が分かる。
渋木宏明(ひどり)さんに感謝。