1

この質問は、管理されていないリソース(COM相互運用)を処理し、リソースリークが発生しないようにすることに関するものです。私が物事を正しい方法で行っているように見えるかどうかについてのフィードバックをいただければ幸いです。

バックグラウンド:


2つのクラスがあるとしましょう。

  • COMオブジェクトのラッパーであるクラスLimitedComResource(一部のAPIを介して受信)。これらのCOMオブジェクトの数は限られているため、私のクラスはIDisposable、COMオブジェクトが不要になったときにCOMオブジェクトを解放する役割を担うインターフェイスを実装します。

  • 別のタイプのオブジェクトは、でManagedObjectいくつかの作業を実行するために一時的に作成されますLimitedComResource。彼らはそうではありませんIDisposable

上記を図にまとめると、私のクラスは次のようになります。

            +---------------+           +--------------------+
            | ManagedObject | <>------> | LimitedComResource |
            +---------------+           +--------------------+
                                                  |
                                                  o IDisposable

(これら2つのクラスのサンプルコードをすぐに提供します。)

質問:


私の一時的なManagedObjectオブジェクトは使い捨てではないので、私は明らかにそれらがどれくらいの期間存在するかを制御することはできません。しかし、その間に私はaが参照しているDisposedを持っているかもしれません。もはやそこにないにアクセスしないようにするにはどうすればよいですか?LimitedComObjectManagedObjectManagedObjectLimitedComResource

            +---------------+           +--------------------+
            | managedObject | <>------> |   (dead object)    |
            +---------------+           +--------------------+

私は現在、弱参照と、LimitedResourceオブジェクトがすでに破棄されているかどうかを示すフラグを組み合わせてこれを実装しています。より良い方法はありますか?

サンプルコード(私が現在持っているもの):


LimitedComResource

class LimitedComResource : IDisposable
{
    private readonly IUnknown comObject;  // <-- set in constructor

    ...

    void Dispose(bool notFromFinalizer)
    {
        if (!this.isDisposed)
        {
            Marshal.FinalReleaseComObject(comObject);
        }
        this.isDisposed = true;
    }

    internal bool isDisposed = false;
}

ManagedObject

class ManagedObject
{
    private readonly WeakReference limitedComResource;  // <-- set in constructor

    ...

    public void DoSomeWork()
    {
        if (!limitedComResource.IsAlive())
        {
            throw new ObjectDisposedException();
            //        ^^^^^^^^^^^^^^^^^^^^^^^
            //  is there a more suitable exception class?
        }

        var ur = (LimitedComResource)limitedComResource.Target;
        if (ur.isDisposed)
        {
            throw new ObjectDisposedException();
        }

        ...  // <-- do something sensible here!
    }
}
4

2 に答える 2

1

いいえ、これは大丈夫ではありません。WeakReference は、マネージド オブジェクトがガベージ コレクションされたことのみを通知します。IDisposable とは何の関係もありません。Dispose() のポイントは、ガベージ コレクターがアンマネージ リソースを解放する前に解放することです。

実際、マネージ オブジェクトが第 1 世代にあり、COM ラッパーが第 0 世代にある場合、深刻な問題が発生します。WeakReference はラッパーを存続させることができません。Dispose() を自分で呼び出す前に、ラッパーが収集され、COM オブジェクトが破棄されます。

管理対象オブジェクトにラッパー オブジェクトへの単純な参照を格納するだけです。ラッパーを収集できるように、Dispose() を呼び出した後に null に設定できます。また、クライアント コードがそれを使用しようとして参照が null の場合は、ObjectDisposedException をスローします。または、それが理にかなっている場合は、それを再作成します。

于 2010-04-30T00:59:13.360 に答える
1

弱参照のターゲットをオブジェクト型にキャストすると、オブジェクトが破棄されている場合は null が返されます。操作を実行する前に、返された値が null かどうかを確認するだけです。ドキュメントの例を参照してください。また、使用の弱い参照の使用に関するこの記事を見つけることもできます。後者の記事からの関連する引用を次に示します。

強い参照を確立してオブジェクトを再度使用するには、WeakReference の Target プロパティをオブジェクトの型にキャストします。Target プロパティが null を返す場合、オブジェクトは収集されています。それ以外の場合は、アプリケーションがオブジェクトへの強い参照を回復したため、引き続きオブジェクトを使用できます。

例:

class ManagedObject 
{ 
    private readonly WeakReference limitedComResource;  // <-- set in constructor 

    ... 

    public void DoSomeWork() 
    { 
        var ur = (LimitedComResource)limitedComResource.Target; 
        if (ur == null) 
        { 
            throw new ObjectDisposedException(); 
        } 

        ...  // <-- do something sensible here! 
    } 
}
于 2010-04-29T21:56:14.443 に答える