我一直在熟悉即将添加到C# 8和.NET Core 3.0的一些内容,但对于正确实现IAsyncDisposable(在撰写本文时,此链接没有提供任何指导),我还不确定。
特别是,当实例未被显式处理时,即它没有被包装在async using(...)
中且没有显式调用.DisposeAsync()
时,这种情况对我来说并不清楚。
我的第一个想法是做与实现IDisposable时相同的事情:
- 我的
DisposeAsync()
实现会使用disposing: true
调用DisposeAsync(bool disposing)
- 实现一个终结器(使用
~MyType()
),它调用DisposeAsync(disposing: false)
DisposeAsync(bool disposing)
实际上释放并/或处理所有东西,并且如果disposing == true
,则禁止终结。
我担心的是,在终结器中没有东西可以等待DisposeAsync(bool)
的结果,而在终结器中显式等待似乎非常危险。
当然,“只是泄漏”看起来也不太理想。
为了具体说明,这里是一个(简化的)示例类,该类确实有一个终结器:
internal sealed class TestAsyncReader: IAsyncDisposable
{
private bool IsDisposed => Inner == null;
private TextReader Inner;
internal TestAsyncReader(TextReader inner)
{
Inner = inner;
}
// the question is, should this exist?
~TestAsyncReader()
{
DisposeAsync(disposing: false);
}
private ValueTask DisposeAsync(bool disposing)
{
// double dispose is legal, but try and do nothing anyway
if (IsDisposed)
{
return default;
}
// should a finalizer even exist?
if (disposing)
{
GC.SuppressFinalize(this);
}
// in real code some resources explicitly implement IAsyncDisposable,
// but for illustration purposes this code has an interface test
if (Inner is IAsyncDisposable supportsAsync)
{
var ret = supportsAsync.DisposeAsync();
Inner = null;
return ret;
}
// dispose synchronously, which is uninteresting
Inner.Dispose();
Inner = null;
return default;
}
public ValueTask DisposeAsync()
=> DisposeAsync(disposing: true);
}
那么,有关于正确处理泄露的 IAsyncDisposable
实例的指导吗?
IAsyncDisposable
到Threading.Timer
的提交链接:Here's the commit。虽然不知道是否有帮助,但这是它的实现示例... - Heretic MonkeySafeHandle
及其派生类更加容易和安全。这样可以规避您的问题。 - canton7Dispose
方法。如果您遵循此模式,则永远不需要将终结器与DisposeAsync
集成。 - Stephen Cleary