mil*_*lie 5 c# dispose idisposable iasyncdisposable
假设我有一个非密封类,它不处理任何非托管资源。我需要在其处置阶段进行一次异步调用来进行一些清理。没有其他托管资源需要处理。
据我了解,为了进行异步清理调用,我必须实现 IAsyncDisposable 并使用 DisposeAsync() 和 DisposeAsyncCore() 方法。但该指南指出,在实现异步处置模式时,您还应该实现处置模式。这一切都很好,但在 Dispose() 中我不需要做任何事情。
所以我的问题是,Dispose() 逻辑应该为空还是我需要一些东西以同步方式进行异步清理?(请参阅代码中关于“如果有什么东西应该放在这里怎么办”的注释)。
public class MyClass : IDisposable, IAsyncDisposable
{
private bool disposed;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public async ValueTask DisposeAsync()
{
await DisposeAsyncCore().ConfigureAwait(false);
Dispose(false);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
// What if anything should go here?
}
disposed = true;
}
}
protected virtual async ValueTask DisposeAsyncCore()
{
// Make async cleanup call here e.g. Database.CleanupAsync();
}
}
Run Code Online (Sandbox Code Playgroud)
对于那些仍然犹豫是否要同时实施这两者的人来说,例子是:
internal class Program
{
static void Main(string[] args)
{
foreach (var a in new B()){}
//IAsyncDisposable is not called - you leaking resources.
//No deadlocks in UI, no warning in compilation, nothing.
//So it is better to be on safe side and implement both
//because you never know how one will manage lifetime of your class.
}
public class B : IEnumerable, IAsyncEnumerable<object>
{
public IEnumerator GetEnumerator() => new A();
public IAsyncEnumerator<object> GetAsyncEnumerator(CancellationToken ct) => new A();
}
public class A : IAsyncEnumerator<object>, IEnumerator
{
public ValueTask DisposeAsync()
{
Console.WriteLine("Async Disposed");
return ValueTask.CompletedTask;
}
public bool MoveNext() => false;
public void Reset(){}
public ValueTask<bool> MoveNextAsync() => ValueTask.FromResult(false);
public object Current => null;
}
}
Run Code Online (Sandbox Code Playgroud)
结论
您可以自由地仅添加对异步版本的支持,但要注意:某些包装,例如foreach旧版本的 DI 容器(Ninject、StructureMap等)、代码生成器(例如RestSharp)或代理生成器(例如Castle.Proxy )可能不支持IAsyncDisposable。未能将对象转换为IDisposable将在您的应用程序中出现难以捕获的错误。然而,如果你确实实现了它,可能发生的最糟糕的事情是finally块中的死锁(如果你通过异步同步来实现)。
一般来说,如果您计划将其公开 API 或者您无法控制类的生命周期(例如在 DI 容器或其他众所周知的包装器中),那么最好支持这两种操作。
如何
有完整的微软示例,介绍如何在可继承类中实现它们(非密封,如您的示例) - https://learn.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing- disposeasync#implement-both-dispose-and-async-dispose-patterns
class ExampleConjunctiveDisposableusing : IDisposable, IAsyncDisposable
{
IDisposable? _disposableResource = new MemoryStream();
IAsyncDisposable? _asyncDisposableResource = new MemoryStream();
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
public async ValueTask DisposeAsync()
{
await DisposeAsyncCore().ConfigureAwait(false);
Dispose(disposing: false);
#pragma warning disable CA1816 // Dispose methods should call SuppressFinalize
GC.SuppressFinalize(this);
#pragma warning restore CA1816 // Dispose methods should call SuppressFinalize
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
_disposableResource?.Dispose();
(_asyncDisposableResource as IDisposable)?.Dispose();
_disposableResource = null;
_asyncDisposableResource = null;
}
}
protected virtual async ValueTask DisposeAsyncCore()
{
if (_asyncDisposableResource is not null)
{
await _asyncDisposableResource.DisposeAsync().ConfigureAwait(false);
}
if (_disposableResource is IAsyncDisposable disposable)
{
await disposable.DisposeAsync().ConfigureAwait(false);
}
else
{
_disposableResource?.Dispose();
}
_asyncDisposableResource = null;
_disposableResource = null;
}
}
Run Code Online (Sandbox Code Playgroud)