簡體   English   中英

C#一次性物品

[英]C# disposable objects

是否有一些關於如何處理IDisposable對象序列的建議?

例如,我有一個構建IEnumerable<System.Drawing.Image>序列的方法,在某些時候我需要手動處理這些對象,否則這可能會導致一些泄漏。

現在,有沒有辦法將Dispose()調用綁定到垃圾回收器操作,因為我希望這些對象在不再可以從其他代碼部分訪問的時刻處理好?

** 或者你可以建議我采取其他方法嗎? **


通常,這似乎是同樣的問題,例如,在沒有共享指針的非托管C++ ,您可以擁有一個方法:

SomeObject* AllocateAndConstruct();

然后你不能確定何時處理它,如果你不使用代碼合同或不在評論中陳述某些內容。

我猜一次性物體的情況大致相同,但我希望有一個合適的解決方案。

(來自這個問題)

現在,有沒有辦法將Dispose()調用綁定到垃圾回收器操作,因為我希望這些對象在不再可以從其他代碼部分訪問的時刻處理好?

當物體超出范圍/范圍時,GC不會立即發生; 這是不確定的。 當GC看到它時,沒有必要做其他任何事情(終結者尚未處理),因為為時已晚。

那么,訣竅是知道你何時完成它,並自己調用Dispose() 在許多情況下using實現了這一點。 例如,您可以編寫一個實現IDisposable的類並封裝一組Image - 並使用包裝您對該封裝對象的using Dispose()的包裝可能Dispose()持有的所有圖像。

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

但是,如果要在UI上顯示數據,這有點棘手。 那里沒有捷徑; 您必須跟蹤每個圖像的完成時間,或接受非確定性的最終確定。

如果要確定處理集合中的對象,則應在每個對象上調用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#中,如果不處理對象,垃圾收集器最終將運行並清理它。

您應該以您不知道何時不再需要資源的方式設計系統。 在最糟糕的情況下,它們最終會在垃圾收集器到達時被處理掉,但IDisposable的意思是你可以提前釋放重要的資源。

例如,當您使用它們的窗口關閉時,或者當您的工作單元完成對它們執行的任何操作時,您可以定義此“早期”。 但在某些時候,某些對象應該“擁有”這些資源,因此應該知道它們何時不再需要。

簡潔的方法是創建自己的實現IDisposable的泛型集合類。 當這個集合類是Disposed()時,如果它實現了IDisposed,請詢問每個元素,如果是,請設置它。

示例(如果您不了解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,因此myList中的所有bitMaps順便說一下:如果你從文件中加載了位圖而你忘了Dispose()你不知道何時可以刪除該文件的位圖。

您可以使用'using'塊來確保IDisposable在塊保留后立即處理。 編譯器會將這些塊封裝到try-finally語句中,以確保在離開塊時調用Dispose。

通過使用終結器,可以使GC調用Dispose方法來處理那些以某種方式“遺漏”的對象。 但是,實現終結器更加昂貴並且會降低垃圾收集效率 - 並且可能降低應用程序的整體性能。 因此,如果有可能,您應該盡量確保自己處理您的IDisposables; 確定性:

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 ++中已知的人工析構函數。

簡潔的方法是創建自己的實現IDisposable的泛型集合類。 當這個集合類是Disposed()時,如果它實現了IDisposed,請詢問每個元素,如果是,請設置它。

示例(如果您不了解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;
            }
            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,因此myList中的所有bitMaps順便說一下:如果你從文件中加載了位圖而你忘了Dispose()你不知道何時可以刪除該文件的位圖。

你可以調用GC.Collect()如果你真的要立即處理這些對象,但根據我的理解,由GC決定是否收集內存。
這反過來將為每個應該釋放的對象調用Finalize()方法。
請注意,如果集合超出范圍,GC將最終收集圖像使用的內存。
如果使用實現IDisposeable的集合,也可以使用using構造。 這將保證在集合超出范圍時(或幾乎在范圍結束之后)准確處理對象。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM