设计一个只有一些实现需要IDisposable的接口

时间:2011-08-30 11:06:11

标签: c# .net

您正在设计界面IFoo

public interface IFoo
{
   void Bar();
}

假设此接口有五种实现方式。其中两个实现也应该实现IDisposable,因为它们使用非托管资源。从调用者的角度来看,如果IFoo实现IDisposable这将是最简单的,因此任何IFoo都可以包含在using块中,但当然一些实现将是散落着空Dispose()方法。只是好奇还有其他方法吗?

7 个答案:

答案 0 :(得分:7)

我怀疑我只是要求IDisposable() - 无操作Dispose()不是一个很大的开销。

如果你不能确定它是否是一次性的,以下是非常有效的:

var mightBeDisposable = GetBlah();
using(mightBeDisposable as IDisposable)
{
   // etc
}

答案 1 :(得分:3)

.NET Framework中有先例用于实现IDisposable的接口 - 例如IComponentIDataReader

当您希望大多数实现需要处理时,这似乎是一种合理的模式。

答案 2 :(得分:2)

虽然实现接口的对象通常承诺在没有这些接口的对象中不常见的能力或特性,虽然IDisposable是一个接口,但是对象实现IDisposable的事实并不承诺任何缺乏对象的能力或特征。相反,对象实现IDisposable的事实意味着它缺少在不实现它的对象中的共同特征:获取或持有引用的任何人的能力放弃它而不考虑它是否可以或应该首先清理对象。

如果使用特定接口类型的代码通常不是最后一个持有引用的东西,那么接口就不需要实现IDisposable。即使某些实现无法安全放弃,对于除持有引用的最后一个实例之外的实例的任何用户都无关紧要。如果该用户通常会更多地了解特定类型而不是接口所隐含的特定类型,则用户将知道该对象是否需要清理,无论接口是否指示它。

另一方面,如果一个对象的最后一个用户除了实现某个接口之外通常不会知道任何事情,那么该接口应该继承IDisposable偶数(可能特别是!)如果只是一小部分实现需要清理。考虑IEnumerableIEnumerable<T>的情况。任何调用IEnumerable<T>.GetEnumerator()的代码都会收到可能是Universe中任何实现IDisposable的对象的唯一引用。因此,该代码承担了确保在该引用上调用Dispose的责任。任何调用IEnumerable<T>.GetEnumerator()并且既没有在返回值上调用Dispose也没有将其提供给其他承诺执行此操作的代码的代码都被破坏了。

非通用IEnumerable.GetEnumerator返回的类型未实现IDisposable。这就是因为它似乎表明调用IEnumerable.GetEnumerator的代码没有责任处理它。不幸的是,这种暗示是不正确的。调用IEnumerable.GetEnumerator的代码有责任确保如果返回的特定实例实现IDisposable,则必须调用其Dispose方法。不履行责任的守则不会比没有处理IEnumerable<T>.GetEnumerator的回报的代码破坏。 IEnumerable.GetEnumerator返回类型(即IEnumerator)未能实现IDisposable并不会消除调用者清理返回对象的责任。它只会使执行此类责任更加繁重,并增加了代码无法执行此操作的可能性。

答案 3 :(得分:1)

您可以创建界面的一次性版本,以更好地显示您的意图:

public interface IDisposableFoo : IFoo, IDisposable
{
}

继承此接口的任何类仍可被视为IFoo。您可能遇到的一个问题是需要在处理之前检查您的IFoo对象是否是一次性版本,但这很容易。

答案 4 :(得分:1)

如果您不介意在调用网站上添加一些代码,可以执行以下操作:

IFoo foo = GetMeSomeFoo();

foo.UseFoo();

var disposableFoo = foo as IDisposable;
if (disposableFoo != null)
    disposableFoo.Dispose();

不漂亮,但不会污染您的界面。不能确保调用者能够完成所有这些工作。

编辑:正如Hans Passant指出的那样,它基本上等于

IFoo foo = GetMeSomeFoo();
using (foo as IDisposable) {
    foo.UseFoo();
} 

答案 5 :(得分:1)

我不会强制IFoo实现IDisposable,因为它反对SOLID。 如果您愿意,可以从IFoo派生IDisposableFoo,或者如果需要,可以进行检查(甚至可以将IFoo包装到DisposableAdapter并检查IDisposable)。

class DisposableAdapter : IDisposable, IFoo
{
   IFoo _obj;
   public DisposableAdapter(IFoo obj)
   {
      _obj = obj;
   }

   public void Dispose()
   {
      if (_obj is IDisposable)
        ((IDisposable)obj).Dispose();
   }     

   // copy IFoos implementations from obj

}
使用

using(var foo = new DisposableAdapter(myFoo)) //... use foo just as you had myFoo

答案 6 :(得分:0)

你需要2个接口,一个实现IDisposable,一个不实现 - IFooIDisposableFoo或只需要IFoo一次性。

IDisposable的一些空实现没有害处。鉴于你已经有一些需要处理它似乎是一个可能的用例。