トップ回答者
PaintEventArgs.Dispose メソッドは呼ぶべきか

質問
-
System.Windows.Forms.Control クラスを継承したクラスを作り、OnPaint メソッドをオーバーライドしましたが、その引数である PaintEventArgs クラスの Dispose メソッドは呼ぶべきなのでしょうか。
MSDN ライブラリやネット上にある OnPaint メソッドのサンプルコードを見ても、誰も Dispose メソッドは呼んでいないように思えます。
しかし、MSDN ライブラリの PaintEventArgs.Dispose の説明には「PaintEventArgs を使い終わったら、Dispose を呼び出します」と書かれています。
どちらなのでしょうか?
回答
-
コード ブロック
private PaintEventArgs _LastInstance;
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
_LastInstance = e;
}protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
if (_LastInstance != null)
{
Graphics g = _LastInstance.Graphics;
_LastInstance = null;
}
}Graphicsプロパティの参照でOutOfMemoryExceptionが発生しました。
デバッガで_LastInstance(PaintEventArgs)のプライベートメンバーを参照すると、graphicsがnullになっています。
このことから、WndProcとOnPaintの間でDisposeされているであろうという動作上の確認はできます。
(ドキュメントでの裏付けとは違うため、不十分かもしれません)
-
豊田 芳彰 さんからの引用 ソースがいつ公開されるか、公開されたソースではたして解決するか分かりませんので、この件はとりあえず 「普通に OnPaint をオーバーライドしたり、イベントハンドラで応答してるだけなら、明示的に Dispose を呼ぶ必要はなさそうだ」 とうことで、保留にしておきます。
年明けに公開されてる予定と聞いていたため「近日」と表現しましたが、米国時間の16日から公開が始まっています。
Visual Studio 2008(Express Editionを除く)でHotfixを当てて、適切に設定すればベースクラスライブラリのソースコードにステップイン等が行えます。
なお、手元にVisual Studio 2008の正式版がないため、私は試せていません。
すべての返信
-
豊田 芳彰 さんからの引用 System.Windows.Forms.Control クラスを継承したクラスを作り、OnPaint メソッドをオーバーライドしましたが、その引数である PaintEventArgs クラスの Dispose メソッドは呼ぶべきなのでしょうか。
MSDN ライブラリやネット上にある OnPaint メソッドのサンプルコードを見ても、誰も Dispose メソッドは呼んでいないように思えます。
OnPaintの外側のWmPaintあたりが処理してくれるのではなかろうかと考えます。
(確認は取っていません)
PaintEventArgsの説明は、別の目的で使用した際にDisposeが必要という意味だと認識しています。
-
豊田 芳彰 さんからの引用 「別の目的」 と 「本来の目的」 とはそれぞれ何でしょうか?
「別の目的」という言い方がまずかったかもしれません。
.NET Frameworkが用意している既存のイベント(OnPaint, Paint)以外の場所、つまり自らPaintEventArgsのインスタンスを生成した場合には必要かと考えます。
あくまでイメージ
コード ブロックpublic PaintEventHandler CustomPaint = delegate { };
private void FireCustomPaint()
{
using (Graphics g = this.CreateGraphics())
using (PaintEventArgs e = new PaintEventArgs(g, this.ClientRectangle))
{
CustomPaint(this, e);
}
}
OnPaintのオーバーライドやPaintイベントの有無を問わず、.NET Frameworkの内側ではPaintEventArgsのインスタンスが生成されているので、誰かがDisposeしていないとまずいことになりますし、この辺はOnPaintの外側のメソッドで責任持ってくれるのではないかなと考えています。
また、派生クラスの派生クラスでbase.OnPaintを先に処理してしまうと、PaintEventArgsが解放されているというのもまずいですし。
そのうち公開されるであろう、.NET Frameworkのソースコードが参照できれば確実だと思います。
(ILDASMという手も考えられますが、今のところは調べていません)
-
コード ブロック
private PaintEventArgs _LastInstance;
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
_LastInstance = e;
}protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
if (_LastInstance != null)
{
Graphics g = _LastInstance.Graphics;
_LastInstance = null;
}
}Graphicsプロパティの参照でOutOfMemoryExceptionが発生しました。
デバッガで_LastInstance(PaintEventArgs)のプライベートメンバーを参照すると、graphicsがnullになっています。
このことから、WndProcとOnPaintの間でDisposeされているであろうという動作上の確認はできます。
(ドキュメントでの裏付けとは違うため、不十分かもしれません)
-
-
豊田 芳彰 さんからの引用 ソースがいつ公開されるか、公開されたソースではたして解決するか分かりませんので、この件はとりあえず 「普通に OnPaint をオーバーライドしたり、イベントハンドラで応答してるだけなら、明示的に Dispose を呼ぶ必要はなさそうだ」 とうことで、保留にしておきます。
年明けに公開されてる予定と聞いていたため「近日」と表現しましたが、米国時間の16日から公開が始まっています。
Visual Studio 2008(Express Editionを除く)でHotfixを当てて、適切に設定すればベースクラスライブラリのソースコードにステップイン等が行えます。
なお、手元にVisual Studio 2008の正式版がないため、私は試せていません。
-
さっそく System.Windows.Forms.dll や System.Drawing.dll のソースコードを覗いてみたところ、ちゃんと Dispose メソッドが呼ばれていました(PaintEventArgs の Dispose が呼ばれ、その中で Graphics の Dispose が呼ばれていた)。
今回の問題は 「自分で Dispose を呼ぶ必要はない(が、呼んでも問題ない)」 ということで解決しました。
ありがとうございました。
.NET Framework の裏側がこうも簡単に覗けるのは素晴らしいです。興味があるので、他の部分も色々覗いてみます。