Class 应该同时实现 IAsyncDisposable 和 IDisposable 吗?

Should Class Implement Both IAsyncDisposable and IDisposable?

例如我有这个 class:

public class UnitOfWork : IUnitOfWork
{
    private readonly ApplicationDbContext _context;

    public IProductRepository Products { get; private set; }
    public ICategoryRepository Categories { get; private set; }
    public IPhotoRepository Photos { get; private set; }

    public UnitOfWork(ApplicationDbContext context, IProductRepository productRepository,
        ICategoryRepository categoryRepository, IPhotoRepository photoRepository)
    {
        _context = context;
        Products = productRepository;
        Categories = categoryRepository;
        Photos = photoRepository;
    }

    public int Complete()
    {
        return _context.SaveChanges();
    }

    public Task<int> CompleteAsync()
    {
        return _context.SaveChangesAsync();
    }

    public void Dispose()
    {
        _context.Dispose();
    }

    public async ValueTask DisposeAsync()
    {
        await _context.DisposeAsync();
    }
}

有了这样的界面:

public interface IUnitOfWork : IDisposable, IAsyncDisposable
{
    IProductRepository Products { get; }
    ICategoryRepository Categories { get; }
    IPhotoRepository Photos { get; }
    int Complete();
    Task<int> CompleteAsync();
}

同时拥有Async 和Sync 方法是否正确?另外,如果我在 asp 网络核心中使用 DI,在处理过程中将调用什么方法。

无论您对 AspNetCore 有何担忧; 是的,在一个 class.

中实现 Sync 和 Async 方法是完全可以的

但是,拥有接口链(一个接口要求实现另一个接口)被认为不是干净的代码。

this

什么时候应该只实施 IDisposable

IDispoable 是更标准和默认的接口,从一开始或之后不久就被几乎所有框架支持。如果您的 class 或 class 的派生 class 不需要异步释放资源,那么请考虑仅实施 IDisposable。例如,SemaphoreSlim 没有实现 IAsyncDisposable.

什么时候可以只实施 IAsyncDisposable

注意:这更适用于您可以控制或知道您的对象将如何实例化和销毁的情况。例如,如果您正在编写一个可执行文件(而不是一个库),您可以在其中更好地控制您的整体环境和代码的命运。

如果您预见到您的 class 或您的 class 的派生 class 将需要或可能需要异步释放资源,您应该实施 IAsyncDisposable

什么时候应该同时实施 IAsyncDisposableIDisposable

注意:以下更多地适用于库的最佳实践和推荐方法,因为它们通常无法控制创建库中定义类型实例的代码。

理想情况下,您应该同时实施 IAsyncDisposableIDisposable。换句话说,IAsyncDisposable 不应被视为 IDisposable.

的替代品

为什么?

  1. 依赖注入框架可能支持也可能不支持IAsyncDisposable。大多数,当然,但不是所有的依赖注入系统都是一样的。如果您正在编写一个库,您不知道如何创建您的对象。
  2. 如果您的代码在容器内运行并且容器是同步处理的,则可能会抛出异常(对于 .NET Core 依赖注入系统而言是正确的)。
  3. 创建一个对象然后在 DisposeAsync 上阻塞以使其同步并不那么干净。调用者的处置调用将更像 yourDisposableObject.DisposeAsync().GetAwaiter().GetResult().

引用微软官方MSDN.

It is typical when implementing the IAsyncDisposable interface that classes will also implement the IDisposable interface. A good implementation pattern of the IAsyncDisposable interface is to be prepared for either synchronous or asynchronous dispose. All of the guidance for implementing the dispose pattern also applies to the asynchronous implementation.

其他次要建议: 如果您预见到 class 的派生 [=95],请考虑将 Dispose()DisposeAsync() 实现为虚拟=]es 将拥有可能需要处理的资源。否则,请考虑让您的 class sealed.

很好的例子

  1. Stephen Toub's Binary Writer class

参考资料

  1. Alistair Evans blog
  2. MSDN
  3. Code Therapist