这是否是类层次结构的"传统"处置模式的合法替代方案?

Kit*_*Kit 2 c# dispose idisposable finalizer boilerplate

我不是样板代码的粉丝:复制粘贴重用可能容易出错.即使您使用代码段或智能模板,也无法保证其他开发人员所做的,这意味着无法保证他们做得对.而且,如果你必须看到代码,你必须理解和/或维护它.

我想从社区中了解到:我对类层次结构的IDispose的实现是否是"传统"处置模式的合法替代?通过合法,我的意思是正确,相当好的表现,健壮和可维护.

我很好,这个替代方案是完全错误的,但如果是,我想知道为什么.

此实现假定您可以完全控制类层次结构; 如果你不这样做,你可能不得不回到样板代码.Add*()的调用通常在构造函数中进行.

public abstract class DisposableObject : IDisposable
{
  protected DisposableObject()
  {}

  protected DisposableObject(Action managedDisposer)
  {
     AddDisposers(managedDisposer, null);
  }

  protected DisposableObject(Action managedDisposer, Action unmanagedDisposer)
  {
     AddDisposers(managedDisposer, unmanagedDisposer);
  }

  public bool IsDisposed
  {
     get { return disposeIndex == -1; }
  }

  public void CheckDisposed()
  {
     if (IsDisposed)
        throw new ObjectDisposedException("This instance is disposed.");
  }

  protected void AddDisposers(Action managedDisposer, Action unmanagedDisposer)
  {
     managedDisposers.Add(managedDisposer);
     unmanagedDisposers.Add(unmanagedDisposer);
     disposeIndex++;
  }

  protected void AddManagedDisposer(Action managedDisposer)
  {
     AddDisposers(managedDisposer, null);
  }

  protected void AddUnmanagedDisposer(Action unmanagedDisposer)
  {
     AddDisposers(null, unmanagedDisposer);
  }

  public void Dispose()
  {
     if (disposeIndex != -1)
     {
        Dispose(true);
        GC.SuppressFinalize(this);
     }
  }

  ~DisposableObject()
  {
     if (disposeIndex != -1)
        Dispose(false);
  }

  private void Dispose(bool disposing)
  {
     for (; disposeIndex != -1; --disposeIndex)
     {
        if (disposing)
           if (managedDisposers[disposeIndex] != null)
              managedDisposers[disposeIndex]();
        if (unmanagedDisposers[disposeIndex] != null)
           unmanagedDisposers[disposeIndex]();
     }
  }

  private readonly IList<Action> managedDisposers = new List<Action>();
  private readonly IList<Action> unmanagedDisposers = new List<Action>();
  private int disposeIndex = -1;
}
Run Code Online (Sandbox Code Playgroud)

这是一个"完整"实现,在某种意义上我提供了对最终化的支持(知道大多数实现不需要终结器),检查对象是否被丢弃等等.真正的实现可能会删除终结器,例如,或者创建一个包含终结器的DisposableObject的子类.基本上,我把所有我能想到的东西扔进了这个问题.

可能有一些我已经错过的边缘情况和深奥的情况,所以我邀请任何人在这种方法中挖洞或用纠正来支撑它.

其他替代方案可能是在DisposableObject中使用单个Queue <Disposer>处理器而不是两个列表; 在这种情况下,当调用处理器时,它们将从列表中删除.我可以想到其他一些细微的变化,但它们具有相同的一般结果:没有样板代码.

Sco*_*man 5

您可能遇到的第一个问题是C#只允许您从单个基类继承,在这种情况下,它始终DisposableObject.在这里,您通过强制其他层来混乱您的类层次结构,以便需要继承的类DisposableObject和其他一些对象可以这样做.

您还将通过此实现引入大量的开销和维护问题(更不用说每当有新的项目出现时重复的培训成本,您必须解释他们应该如何使用此实现而不是定义的模式) .你知道有多个状态可以跟踪你的两个列表,没有对动作调用的错误处理,调用动作时的语法看起来很"奇怪"(虽然调用数组中的方法可能很常见,在数组访问之后简单地放置()的语法看起来很奇怪).

我理解减少你必须编写的样板数量的愿望,但可处置性通常不是我建议采用捷径或偏离模式的那些领域之一.我通常得到的最接近的是使用辅助方法(或扩展方法)将实际调用包装到Dispose()给定对象上.这些调用通常如下所示:

if (someObject != null)
{
   someObject.Dispose();
}
Run Code Online (Sandbox Code Playgroud)

这可以使用辅助方法简化,但请记住,FxCop(或任何其他检查正确的dispose实现的静态分析工具)会抱怨.

就性能而言,请记住,您正在使用此类实现进行大量委托调用.就委托人而言,这比普通的方法调用要贵一些.

可维护性肯定是一个问题.正如我所提到的,每当有新的项目进入项目时,您就会有重复的培训成本,并且您必须解释他们应该如何使用此实现而不是定义的模式.不仅如此,每个人都记得将一次性对象添加到列表中时遇到问题.

总的来说,我认为这样做是一个坏主意,会导致很多问题,特别是随着项目和团队规模的增加.