IDisposable鑒于實作類包含 -Member,實作 -Pattern 的正確方法是什么GCHandle?
我想出了這個,但它導致我的應用程式泄漏記憶體:
public class foo : IDisposable
{
private bool disposed = false;
private readonly byte[] bytes;
private readonly GCHandle hBytes;
private readonly IDisposable someWrapperForUnmanagedData;
public foo(byte[] bytes)
{
this.bytes = bytes;
hBytes = GCHandle.Alloc(bytes, GCHandleType.Pinned);
someWrapperForUnmanagedData = new bar(..., bytes, ...);
}
protected virtual void Dispose(bool disposing)
{
if (disposed) return;
if (disposing)
{
hBytes.Free();
}
someWrapperForUnmanagedData.Dispose();
disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SupressFinalize(this);
}
~foo() => Dispose(false);
}
Microsoft 檔案沒有提到 GCHandles 應該如何與 一起使用IDisposable,而且我似乎在網上找不到任何東西。
終結器應該foo呼叫hBytes.Free()嗎?按照檔案,終結器只應該為非托管資源呼叫清理例程,在這種情況下,它只是someWrapperForUnmanagedData.
uj5u.com熱心網友回復:
你的Dispose方法倒退了。
- 當你的型別的終結器被呼叫時,你應該釋放你擁有的托管資源。
- 當你的型別的
IDisposable.Dispose()方法被呼叫時,你應該呼叫Dispose你擁有的東西,并釋放你擁有的托管資源。
原因很簡單。令人高興的情況是您的型別已被處理(有人呼叫.Dispose()它)。這使您有機會釋放您擁有的非托管資源,并且您還需要將此Dispose()呼叫傳播給您的孩子,以便他們也可以釋放他們的非托管資源。
如果有人忘記呼叫.Dispose()你,那么 GC 可能會通過呼叫你的 finalize 方法來拯救你。在這種情況下,你應該釋放你擁有的任何非托管資源,但你不應該呼叫你的任何孩子。如果您的任何孩子有自己的終結器,那么:
- GC 會單獨呼叫它們的終結器,所以你也不應該呼叫它。
- 無法保證型別最終確定的順序,因此您的孩子可能已經最終確定。
請注意所有權一詞的使用。非托管資源應該只有一個擁有它的東西,那就是負責釋放它的東西。
請注意,您someWrapperForUnmanagedData不是非托管型別。這是一個 C# 類,非常易于管理。非托管型別類似于原始指標。如果someWrapperForUnmanagedData它自己擁有其他一些非托管型別,它應該實作IDisposable并定義一個終結器,以確保釋放這些型別。
GCHandle沒有自己的終結器,因此它無法被Free()GC 編輯。因此,它在這里算作非托管資源:您需要手動呼叫Free()它。
所以:
protected virtual void Dispose(bool disposing)
{
if (disposed) return;
if (disposing)
{
someWrapperForUnmanagedData.Dispose();
}
hBytes.Free();
disposed = true;
}
請注意,正如本檔案明確指出的那樣,您應該盡可能使用 a SafeHandle(或其子類之一)。SafeHandle實作它自己的終結器,所以你不必這樣做。
運行時也知道SafeHandle,這讓它避免了一些真正令人討厭的競爭條件,例如,您的型別可以在它進行非托管呼叫的同時完成,從而導致嚴重的崩潰。如果您不了解這場比賽,這意味著您絕對應該使用SafeHandle.
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/450971.html
上一篇:System.Text.Json無法反序列化List<List<object>>
下一篇:很難調整字串以填充類
