Out of Memory

本ブログは更新を停止しました。Aerieをよろしくお願いいたします。

目次

Blog 利用状況

ニュース

2009年3月31日
更新を停止しました。引き続きAerieを御愛顧くださいませ。
2009年2月3日
原則としてコメント受付を停止しました。コメントはAerieまでお願いいたします。
詳細は2月3日のエントリをご覧ください。
2008年7月1日
Microsoft MVP for Developer Tools - Visual C++ を再受賞しました。
2008年2月某日
MVPアワードがVisual C++に変更になりました。
2007年10月23日
blogタイトルを変更しました。
2007年7月1日
Microsoft MVP for Windows - SDKを受賞しました!
2007年6月20日
スキル「ニュース欄ハック」を覚えた!
2006年12月14日
記念すべき初エントリ
2006年12月3日
わんくま同盟に加盟しました。

カレンダー

中の人

αετο? / aetos / あえとす

シャノン? 誰それ。

顔写真

埼玉を馬鹿にする奴は俺が許さん。

基本的に知ったかぶり。興味を持った技術に手を出して、ちょっと齧りはするものの、それを応用して何か形にするまでは及ばずに飽きて放り出す人。

書庫

日記カテゴリ

Dispose…どうする?(その2)

http://blogs.wankuma.com/shannon/archive/2007/01/04/54639.aspxの続き。

最初は、こんなクラスを作って対処しようと考えた。


public class OptimizedBrush : Brush
{
    private readonly Color _color = Color.Empty;
    private readonly Brush _brush = null;
    public OptimizedBrush(Color color)
    {
        _color = color;
        if (color.IsSystemColor)
        {
            _brush = SystemBrushes.FromSystemColor(color);
        }
        else
        {
            _brush = new SolidBrush(color);
        }
    }
    public OptimizedBrush(SolidBrush brush)
    {
        _color = brush.Color;
        _brush = (Brush)brush.Clone();
    }
    public override object Clone()
    {
        return new OptimizedBrush(_color);
    }
    public void Dispose()
    {
        if (!_color.IsSystemColor)
        {
            _brush.Dispose();
        }
    }
    ~OptimizedBrush()
    {
        Dispose();
    }
}

が、またしても撃沈。
おかしいとは思っていたが、この派生クラスのインスタンスをGraphicsのメソッドに渡したとして、そこから色を取得する方法がない。
Brushクラスの説明には、派生クラスの要件として「Cloneを実装せよ」としか書かれていない。
Cloneはしっかり実装しているんだからこれで間違えていないはずだ。ドキュメントが正しければ。

気になったので、ReflectorでSystem.Drawing.BrushおよびSolidBrushの実装を見てみた。

まず、Disposeで落ちた原因だが、やはり予想通り。
SolidBrushはプライベート変数immutableを持っており、これがtrueだとDisposeで例外が飛ぶ。
SystemBrushesから取得したブラシはimmutableがtrueになっていた。

次に、SolidBrushから色を取り出す方法について。
これは.NET 1.1と2.0で微妙に実装が違う。
SolidBrushはコンストラクタ内部で引数のColorからGDIネイティブブラシを作り、これをBrush.SetNativeBrushメソッドに渡している。
1.1ではBrush内部でネイティブブラシハンドルをinternal変数に保持しておき、Graphicsはこれを参照する。
2.0ではより行儀正しく、ネイティブブラシハンドルはprivate変数として保持され、readonlyプロパティを通じてGraphicsに渡される。
で、ここからが問題なのだが…。
Brush.SetNativeBrushは2.0ではprotectedメソッドとして公開されたが、1.1ではinternal。つまり派生クラスから呼び出せない。
よって、.NET 1.1でBrush派生クラスを作る方法は存在しない
確かに、.NET 1.1のBrushクラスの説明には、派生時の要件とかは書いてないのだけれど…派生するなともどこにも書いてないしなぁ。
派生できないならsealedにしとけよ….NET Framework内ならsealedクラスから派生するようなインチキを他ではいっぱいやってるんだからさぁ。
2.0では、SetNativeBrushが公開されたので、Brush派生クラスは作れるようになった。
が、今回の目的は既存のBrushのラッパーを作ることだ。そして、既存のBrushからネイティブブラシハンドルを取得する方法はない
よって、派生クラスは作れるが今回の目的には合致しない。

結局、以下のようなクラスを作ることで目的は達した。


public class OptimizedBrushWrapper : IDisposable
{
    private readonly Color _color = Color.Empty;
    private readonly Brush _brush = null;
    public OptimizedBrushWrapper(Color color)
    {
        _color = color;
        if (color.IsSystemColor)
        {
            _brush = SystemBrushes.FromSystemColor(color);
        }
        else
        {
            _brush = new SolidBrush(color);
        }
    }
    public OptimizedBrushWrapper(SolidBrush brush)
    {
        _color = brush.Color;
        _brush = (Brush)brush.Clone();
    }
    public Brush Brush
    {
        get
        {
            return _brush;
        }
    }
    public void Dispose()
    {
        if (!_color.IsSystemColor)
        {
            _brush.Dispose();
        }
    }
    ~OptimizedBrushWrapper()
    {
        Dispose();
    }
}

OptimizedBrushWrapper.Brush.Dispose();とかされると依然として落ちる可能性があるし、IDisposableなクラスのラッパーを作るのは気が進まないのだが、それは仕方あるまい。

そもそもが、IDisposableを実装するくせにDisposeできないようなクラスを作る奴が悪いのだ。
これでまた一歩、IDisposableが使い物にならなくなったというわけだな。

投稿日時 : 2007年1月5日 10:09

Feedback

# re: Dispose…どうする?(その2) 2007/01/05 11:42 シャノン

RealProxyを使って、システムカラーの場合にだけDisposeをしないプロキシを作った方がきれいかも知れない。

# re: Dispose…どうする?(その2) 2007/01/05 12:07 シャノン

SystemBrushesから取得したブラシでも、Brush.Finalizeが呼ばれる時にはちゃんとDisposeされる。
プロキシではこのケースもスルーした方がいいのか。
試してみた限りでは、プロキシのInvokeに、引数付きのDisposeは来なかったけどなぁ。

# re: Dispose…どうする?(その2) 2007/01/05 13:10 シャノン

待て待て。
そもそも、システムブラシを使う目的はパフォーマンスの向上だったはずだ。
プロキシなんか入れたら本末転倒じゃないのか。

# re: Dispose…どうする?(その2) 2007/04/11 14:00 通りすがり

書かれたコードですが、new したシステムカラーを持つブラシでコンストラクトすると、Clone されるのに Dispose されないですね。
SystemBrushes で得た場合はフラグを立てておき、そのフラグで Dispose するかどうかを判断する方がシンプルではないでしょうか?

# re: Dispose…どうする?(その2) 2007/04/11 15:09 シャノン

あぁ、なるほど。確かに。
ちなみにこの件は、面倒くさくなったのでシステムカラーの場合でも new SolidBrush してしまう方法で逃げました。

# I used to be recommended this website through my cousin. I am now not positive whether or not this submit is written via him as no one else recognise succh designated about my trouble. You are amazing! Thanks! 2019/07/12 3:48 I used to be recommended this website through my c

I used to be recommended this website trough my cousin. Iam now not positive whether oor
not this submit is written via him as no one else recognise such designated
about my trouble. You are amazing! Thanks!

タイトル
名前
Url
コメント