已更改班级名称以保护无辜的。
如果我有一个名为ISomeInterface的接口。我还有继承接口的类,FirstClass和SecondClass。 FirstClass使用必须处理的资源。 SecondClass没有。
所以问题是,我应该从IDisposable继承哪里?以下两个选项似乎都不太理想:
1)让FirstClass继承IDisposable 。然后,任何处理ISomeInterfaces的代码都必须知道是否要处理它们。这闻起来像是与我的紧密联系。
2)让ISomeInterface继承IDisposable 。然后,任何从它继承的类都必须实现IDisposable,即使没有任何东西可以处理。除了注释之外,Dispose方法基本上是空白的。
#2对我来说似乎是正确的选择,但我想知道是否有其他选择。
答案 0 :(得分:20)
如果抽象实体(接口或抽象类)可能有可能是一次性的,它应该实现它。 Stream
,例如本身需要IDisposable
,IEnumerator<T>
也不是......
抽象基类可能更简单,因为您可以使用Dispose()
的默认(空)实现,也可以使用终结器/ Dispose(bool)模式,即
void IDisposable.Dispose() { Dispose(true); GC.SuppressFinalize(this); }
protected virtual void Dispose(bool disposing) {}
~BaseType() {Dispose(false);}
答案 1 :(得分:3)
如果你知道ISomeInterface的某些实现需要处理,那么接口应该继承IDisposable,即使接口的具体实现没有任何可处理的东西。
例如,在BCL中,IDataReader实现了IDisposable,即使人们可以想象没有需要处理的外部资源的数据读取器实现。
答案 2 :(得分:2)
这取决于你的界面,但我倾向于#2。如果你有两个ISomeInterface
的实现,只有一个需要处理,那么你可能需要重构。
通常,当您绑定到接口时,最好让该接口继承IDisposable
而不是基类;如果您的接口没有继承IDisposable
,则必须强制转换为IDisposable
以处置该对象,并且存在InvalidCast的风险......
答案 3 :(得分:2)
如果您希望所有代码一般都处理ISomeInterfaces,那么是的,它们都应该是一次性的。
如果没有,那么创建FirstClass的代码应该处理它:
using (FirstClass foo = new FirstClass()) {
someObjectThatWantsISomeInterface.Act(foo);
}
否则,你总是可以使用类似这种扩展方法的东西:
public static void DisposeIfPossible(this object o) {
IDisposable disp = o as IDisposable;
if (disp != null)
disp.Dispose();
}
// ...
someObject.DisposeIfPossible(); // extension method on object
我还应该提一下,我更喜欢模板基类方法。我在this blog中纠正了这个问题,正确地建造了一次性物品。
答案 4 :(得分:1)
我的建议是转到根,而不是直接转到具体的类。第2点是根,你是由FirstClass的某种契约驱动的。如果您知道类必须实现某个接口,那么您希望确保他们签署合同的接口继承了IDisposable
答案 5 :(得分:1)
到目前为止所写的所有答案都错过了一个关键点:只有基本类型或基本接口才能实现IDisposable
,如果它可能是那些需要基础的代码 - 否则,类实例可能获取实例的所有权,该实例需要在不意识到的情况下进行处理。可能发生这种情况的最常见情况是使用工厂方法;一个主要的例子是IEnumerable<T>
/ IEnumerator<T>
。大多数枚举器不需要清理,但调用IEnumerable<T>.GetEnumerator
的代码通常没有特别的理由相信返回的枚举器实际上需要清理,也不相信它不会被清除。让IEnumerator<T>
的所有实现实现IDisposable
并让所有消费者在返回的枚举器上调用Dispose
比让消费者检查返回的类型是否实现{通常更快{1}}如果是,请调用它。
如果预期基类型引用通常仅由不负责清理有问题项的方法使用,则基本类型不需要实现IDisposable
。负责清理的代码将知道它处理实现IDisposable
的对象是否与基类型有关。