[英]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.