プログラミングに関する小ネタ、Tips、その他色々

概要

  • IDisposableを実装したクラスを、複数クラス間で共有して利用する。
  • QueryInterfaceメソッドで、参照のコピーを取得。
  • Disposeを行うことで、参照カウントが減算される。

巨大な画像リソースを使いまわしたいケースなどを想定。
生成者と利用者が分散、かつ利用者が複数クラスにまたがる場合を想定。
重たいリソースをコピーせずに共有するため。

厳密なリソース管理をする必要がないならば、WeakReferenceを利用するのも手。
利用中は強参照、利用していなければ弱参照として扱うことができ、
参照頻度が高い間は使いまわすことができ、参照頻度が低い場合はGCによって回収される。
※管理がGC頼みになってしまうので、消費リソース量が多い場合は取るべきではないと考えている。
 そもそも弱参照は使い所を選ばないと、何かあった時のメンテナンスが大変・・・

ソース

利用例
// Bitmapを共有
Shared<Bitmap> tmp;
Shared<Bitmap> bmp = new Shared<Bitmap>(new Bitmap(100, 100));
// bmpインスタンスをコピー
tmp = bmp.QueryInterface(); // 参照カウント+1
// 〜bmpインスタンスを利用〜
bmp.Dispose(); // ここではBitmapはまだDisposeされない
// 〜tmpインスタンスを利用〜
tmp.Dispose(); // ここでBitmapがDisposeされる
共有クラス
/// <summary>共有オブジェクトクラス</summary>
/// <typeparam name="TDisposable">共有対象クラス</typeparam>
public class Shared<TDisposable> : IDisposable where TDisposable : class, IDisposable
{
    private TDisposable _target;
    /// <summary>共有対象オブジェクト</summary>
    public TDisposable Target { get { return _target; } }
    /// <summary>参照カウンタクラス</summary>
    private SharedCounter _counter = new SharedCounter();
    /// <summary>開放済みかどうか</summary>
    public bool IsDisposed { get { return _counter.IsDisposed; } }

    /// <summary>コンストラクタ</summary>
    /// <param name="target">共有対象オブジェクト</param>
    public Shared(TDisposable target)
    {
        _target = target;
        _counter.Addref();
    }
    /// <summary>コンストラクタ(参照コピー用)</summary>
    /// <param name="target">共有対象オブジェクト</param>
    /// <param name="counter">参照カウンタ</param>
    private Shared(TDisposable target, SharedCounter counter)
    {
        _target = target;
        _counter = counter;
        _counter.Addref();
    }

    /// <summary>新たな参照を取得する。</summary>
    /// <returns></returns>
    public Shared<TDisposable> QueryInterface()
    {
        return new Shared<TDisposable>(_target, _counter);
    }

    #region IDisposable メンバー
    /// <summary>参照カウントを減算し、参照がなくなればDisposeする。</summary>
    public void Dispose()
    {
        // 開放済みであれば何もしない
        if (_counter.IsDisposed) return;
        // 参照を削除する。
        _counter.Release();
        if (!_counter.IsFree) return;

        if (_target != null)
        {
            _target.Dispose();
            _target = null;
            _counter.IsDisposed = true;
        }
    }
    #endregion

    #region 共有カウンタ
    /// <summary>共有カウンタクラス</summary>
    private class SharedCounter
    {
        /// <summary>カウンタ</summary>
        private int _count;
        /// <summary>参照がないかどうか</summary>
        public bool IsFree { get { return _count <= 0; } }
        /// <summary>開放済みかどうか</summary>
        public bool IsDisposed { get; set; }
        /// <summary>参照の追加</summary>
        public void Addref() { _count++; }
        /// <summary>参照の削除</summary>
        public void Release() { _count--; }
    }
    #endregion
}

コメントをかく


「http://」を含む投稿は禁止されています。

利用規約をご確認のうえご記入下さい

Menu

カテゴリ

プログラミング言語

スクリプト言語

プラットフォーム

ライブラリ

その他

編集テスト用メニュー

【メニュー編集】

管理人/副管理人のみ編集できます