C# 中的 IDisposable 实现 - 是注入的 IDisposable OK 的可选处置

本文关键字:IDisposable OK 注入 中的 实现 | 更新日期: 2023-09-27 18:36:06

如果我有一个相当标准的抽象类,它允许注入一个IDisposable实例。但是,从此类继承的某些类不应像其他类一样释放注入的存储库。显而易见的解决方案是拥有一个构造函数:

public abstract class WorkspaceViewModel : IDisposable
{
    readonly bool _cascadeDisposeRepository;
    protected WorkspaceViewModel(IRepository repository, bool cascadeDisposeRepository=true)
    {
        _repository = repository;
        _cascadeDisposeRepository = cascadeDisposeRepository;
    }

编辑:

我还有一个构造函数

    protected WorkspaceViewModel()
        :this(new RepositoryA(), true){} 

结束编辑

然后以推荐的微软方式实现 Dispose 方法

protected virtual void Dispose(bool disposing)
{
    if (!_disposed)
    {
        if (disposing)
        {
            if (_cascadeDisposeRepository) { _repository.Dispose(); }
            .....

但是,我从未见过以这种方式实现IDisposable,并且想知道这是否是不好的做法(如果是,为什么以及还有哪些其他解决方案更可取)。

谢谢你的想法。

编辑:Mark 的评论让我意识到,更好的实现可能是没有无参数构造函数,强制任何从 WorkspaceViewModel 继承的类创建和释放自己的实例(并选择实现 IDisposable),同时从 WorkspaceViewModel 的实现接口中删除 IDisposable。

C# 中的 IDisposable 实现 - 是注入的 IDisposable OK 的可选处置

由于您对非托管资源没有句柄,因此我建议完全删除 IDisposable。

由于您的类是一个存储库,我怀疑它使用数据库连接,该连接可能间接地对非托管资源具有句柄 - 因此请记住将其包装在:

using(var myConn = new Connection(connectionString))
{
}

然后,您可以让托管代码的美感担心要销毁和保持活动状态 - 您甚至不需要考虑它。

最后一个"使用"实现IDisposable的对象,通过直接访问它或将其传递给其他实体进行已知持续时间的临时使用,应该调用Dispose。 如果Moe从其他实体传递IDisposable,该实体本质上知道Moe何时完成它,那么Moe通常可以期望其他实体照顾它,而不需要调用Dispose本身。 事实上,如果另一个实体在萌用完后可能用于IDisposable,那么萌一定不能自己调用Dispose。 如果Moe传递接口或基类类型的引用,则接口或基类无需实现IDisposable;即使传入的实例可能是实现IDisposable的派生类型,但这不是Moe关心的问题。 创建该实例的实体应该知道它是一个实现IDisposable并处理它的类型。

事情变得棘手的地方是工厂方法。 如果工厂方法可能返回实现IDisposable的对象,并且该方法的调用者是唯一知道何时不再需要该对象的东西,那么该方法的返回类型本身应该实现IDisposable。 由于非泛型IEnumerator接口不遵循该模式,因此需要使用非泛型IEnumerable接口的代码来检查GetEnumerator() implenments 返回的每个对象是否IDisposable,如果是,则对其调用Dispose;这比无条件调用IDisposable更不方便和慢,如果它被IEnumerator继承[即使只有 99.9% 的IEnumerator实现具有无所事事Dispose方法,调用已知接口支持的无所事事方法比检查接口是否支持方法更快]。 请注意,让工厂方法的返回类型实现或继承IDisposable不会给调用方增加任何责任 - 它只会使调用方更容易执行他们将承担的责任