Реализация 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();
}
}
1 ответ
Пример для тех, кто все еще не решается реализовать оба:
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;
}
}
Заключение
Вы можете свободно добавить поддержку только асинхронной версии, но будьте осторожны: некоторые обертки, напримерforeach
или более старые версии контейнеров DI (Ninject, StructureMap и т. д.), генераторы кода, такие как RestSharp , или генераторы прокси, такие как Castle.Proxy , могут не поддерживатьIAsyncDisposable
. Не удалось передать объектIDisposable
представит трудно поймать ошибки в вашем приложении. Принимая во внимание, что если вы реализуете это, худшее, что может случиться, — это взаимоблокировка в блоке finally (если вы сделаете это с помощью sync-over-async).
В общем, лучше поддерживать обе операции , если вы планируете сделать его общедоступным API или у вас нет контроля над временем жизни вашего класса (например, в контейнерах DI или других широко известных оболочках).
Как
Существует полный пример Microsoft о том, как реализовать их оба в наследуемом классе (незапечатанный, как в вашем примере) - https://learn.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-disposeasync# реализация-оба-распоряжения-и-асинхронные-распоряжение-шаблоны
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;
}
}