7

IDisposableオブジェクト シーケンスの処理方法に関するアドバイスはありますか?

たとえば、IEnumerable<System.Drawing.Image>シーケンスを構築するメソッドがあり、ある時点でそのオブジェクトを手動で破棄する必要があります。そうしないと、リークが発生する可能性があるためです。

さて、これらのオブジェクトを他のコード部分からアクセスできなくなっDispose()た瞬間に破棄したいので、呼び出しをガベージ コレクター アクションにバインドする方法はありますか?

**または、他のアプローチについてアドバイスをいただけないでしょうか? **


一般に、これは同じ問題のようです。たとえば、 unmanaged C++without shared pointersでは、メソッドを使用できます。

SomeObject* AllocateAndConstruct();

コード コントラクトを使用しないか、コメントに何かを記載しないと、いつ破棄するかわかりません。

使い捨てオブジェクトの状況はかなり同じだと思いますが、これに対する適切な解決策があることを願っています.

4

6 に答える 6

7

(質問より)

Dispose() 呼び出しをガベージ コレクター アクションにバインドする方法はありますか? これらのオブジェクトは、他のコード部分からアクセスできなくなった瞬間に破棄したいのでしょうか?

オブジェクトがスコープ/リーチの外に出ても、GC はすぐには実行されません。それは非決定論的です。GC がそれを確認するまでには、手遅れであるため、(まだファイナライザーによって処理されていない) 他の処理を行う意味がありません。

秘訣は、いつそれが終わったかを知り、自分自身に電話することDispose()です。多くの場合using、これを達成します。たとえばIDisposable、一連のImages - を実装してカプセル化するクラスを記述し、そのカプセル化オブジェクトの使用を でラップすることができますusingDispose()ラッパーには、すべてのDispose()画像が保持されている可能性があります。

すなわち

using(var imageWrapper = GetImages()) {
    foreach(var image in imageWrapper) {
         ...
    }
    // etc
} // assume imageWrapper is something you write, which disposes the child items

ただし、UI にデータを表示している場合、これは少し厄介です。そこに近道はありません。各画像の処理がいつ完了したかを追跡するか、非決定論的なファイナライズを受け入れる必要があります。

于 2011-04-26T10:02:09.327 に答える
6

コレクション内のオブジェクトを決定的に破棄したい場合は、Disposeそれぞれを呼び出す必要があります。

myImages.ToList().ForEach(image => image.Dispose());

これを行わず、オブジェクトに到達できなくなった場合、最終的に GC が実行されてオブジェクトが解放されます。

呼び出しを手動でコーディングしたくない場合はDispose、実装するラッパー クラスを作成し、それをステートメントIDisposableで使用できます。using

using (myImages.AsDisposable()) { 
  // ... process the images
}

これが必要な「インフラストラクチャ」です。

public class DisposableCollectionWrapper<D> : IDisposable
where D : IDisposable {

  private readonly IEnumerable<D> _disposables;

  public DisposableCollectionWrapper(IEnumerable<D> disposables) {
    _disposables = disposables;
  }

  public void Dispose() {
    if (_disposables == null) return;
    foreach (var disposable in _disposables) {
      disposable.Dispose();
    }
  }

}

public static class CollectionExtensions {

  public static IDisposable AsDisposable<D>(this IEnumerable<D> self)
  where D : IDisposable {
    return new DisposableCollectionWrapper<D>(self);
  }

}

また、これはC++ で説明した状況と同じではないことに注意してください。C++ では、deleteオブジェクトを使用しないと、真のメモリ リークが発生します。C# では、オブジェクトを破棄しないと、最終的にガベージ コレクターが実行されてクリーンアップされます。

于 2011-04-26T10:02:53.513 に答える
5

リソースがいつ不要になるかがわかるようにシステムを設計する必要があります。最悪の場合、ガベージ コレクターが到達した時点で最終的に破棄されますが、IDisposable のポイントは、重要なリソースを早期に解放できることです。

この「早期」は、定義するのはあなた次第です。たとえば、それらを使用しているウィンドウが閉じたとき、または作業単位がそれらに対する操作を終了したときに、それらを解放できます。しかし、ある時点で、一部のオブジェクトがこれらのリソースを「所有」する必要があり、したがって、それらがいつ不要になるかを知る必要があります。

于 2011-04-26T10:02:55.563 に答える
1

'using'ブロックを使用して、ブロックが離れるとすぐにIDisposableが破棄されることを確認できます。コンパイラは、そのようなブロックをtry-finallyステートメントにカプセル化して、ブロックを離れるときにDisposeが呼び出されることを確認します。

ファイナライザーを使用することで、何らかの理由で「欠落」したオブジェクトに対して、GCにDisposeメソッドを呼び出させることができます。ただし、ファイナライザーを実装すると、コストが高くなり、ガベージコレクションの効率が低下します。アプリケーションの全体的なパフォーマンスが低下する可能性があります。したがって、可能であれば、IDisposableを自分で廃棄するようにしてください。決定論的に:

public class Context : IDisposable {

    List<IDisposable> m_disposable = new List<IDisposable>();
    public void AddDisposable(IDisposable disposable) {
        m_disposable.Add(disposable); 
    }

    public void Dispose() {
        foreach (IDisposable disp in m_disposable)
            disp.Dispose(); 
    }

    // the Context class is used that way: 
    static void Main(string[] args) {

        using (Context context = new Context()) {
            // create your images here, add each to the context
            context.AddDisposable(image); 
            // add more objects here 

        } // <- leaving the scope will dispose the context
    }
}

巧妙な設計を使用することで、コンテキストにオブジェクトを追加するプロセスがさらに簡単になる場合があります。作成メソッドにコンテキストを与えるか、静的シングルトンを介して公開することができます。そうすれば、子メソッドスコープでも利用できるようになります-周りのコンテックスへの参照を渡す必要はありません。このスキームを利用することで、C++で知られているfeのような人工的なデストラクタ機能をシミュレートすることも可能です。

于 2011-04-26T10:19:30.943 に答える
1

適切な方法は、IDisposable を実装する独自のジェネリック コレクション クラスを作成することです。このコレクション クラスが Disposed() の場合、各要素が IDisposed を実装しているかどうかを確認し、実装している場合は Dispose します。

例 (IDisposable パターンがわからない場合は、他の場所を参照してください)

public class MyDisposableList<T> : List<T> : IDisposable
{
    private bool disposed = false;

    ~MyDisposableList()
    {
        Dispose(false);
    }

    public void Dispose()
    {
        Dispose(true);
    }

    protected void Dispose(bool disposing)
    {
        if (!disposed)
        {
            foreach (T myT in this)
            {
                IDisposable myDisposableT = myT as IDisposable;
                if (myDisposableT != null)
                {
                    myDisposableT.Dispose();
                }
                myT = null;
            }
            this.Clear();
            this.TrimExcess();
            disposed = true;
        }
    }
    ...
}

利用方法:

using (MyDisposableList<System.Drawing.Bitmap> myList = new ...)
{
    // add bitmaps to the list (bitmaps are IDisposable)
    // use the elements in the list
}

using ステートメントの最後で自動的に myList が Dispose されるため、myList 内のすべての bitMap が Dispose されます。ちなみに、ファイルからビットマップをロードし、そのビットマップを Dispose() するのを忘れた場合、いつそのファイルを削除できるかわかりません。

于 2014-05-05T10:15:11.757 に答える
-2

GC.Collect()本当にこれらのオブジェクトをすぐに破棄する場合は呼び出すことができますが、私の理解では、メモリを収集するかどうかを決定するのは GC 次第です。これにより、解放する必要がある各オブジェクト
のメソッドが呼び出されます。 コレクションが範囲外になると、GC は最終的にイメージによって使用されるメモリを収集することに注意してください。 IDisposeable を実装するコレクションを使用する場合は、using コンストラクトも使用できます。これにより、コレクションがスコープ外になったとき (またはスコープの終わり近く) にオブジェクトが破棄されることが保証されます。Finalize()

于 2011-04-26T10:00:27.010 に答える