从如下情况出发:
public interface ISample
{
}
public class SampleA : ISample
{
// has some (unmanaged) resources that needs to be disposed
}
public class SampleB : ISample
{
// has no resources that needs to be disposed
}
该类SampleA应该实现资源的释放接口IDisposable接口。 你可以通过两种方式解决这个问题:
1.添加所需的接口类SampleA:
public class SampleA : ISample, IDisposable
{
// has some (unmanaged) resources that needs to be disposed
}
2.将它添加到界面ISample并迫使派生类来实现它:
public interface ISample : IDisposable
{
}
如果你把它放到界面,你强迫任何实施实现IDisposable即使她们没什么处置。 在另一方面,这是很清楚的看到界面的具体实施需要处置/使用块,你并不需要强制转换为IDisposable的清理。 可能有一些更多的优点/缺点在这两个方面...为什么你会建议使用优先于其他的一种方式?
如果你申请的using(){}
模式到所有的接口,最好有ISample
派生自IDisposable
,因为凭经验界面设计时是有利于“易用性,使用”过度“易用性的实现”。
继Inteface分离原则的SOLID如果添加了IDisposable你是给方法是不感兴趣的,以便客户端的界面,你应该把它添加到A.
除此之外,接口是从来没有一次性可处理性,因为东西是与混凝土接口的实现相关,从不与接口本身。
任何接口可以具有或不具有需要被设置的元件被潜在实现。
就个人而言,如果所有ISample
的应该是一次性的我把它的接口上,如果只有一些是我只把它放在哪里班应该是。
听起来像是你有后一种情况。
接口IFoo
可能应该实现IDisposable
,如果有可能,至少有一些一些实现将实现IDisposable
,并至少在某些情况下,最后的生存参考实例将被存储在类型的变量或字段IFoo
。 几乎可以肯定应该实现IDisposable
如果任何实现可能实现IDisposable
和实例将通过工厂接口创建(如与实例的情况下IEnumerator<T>
它在许多情况下是通过工厂接口创建IEnumerable<T>
比较IEnumerable<T>
和IEnumerator<T>
是有益的。 其中实现某些类型IEnumerable<T>
也实现IDisposable
,但它创建这样的类型的实例代码就知道它们是什么,知道他们需要处理,并把它们作为自己的特定类型。 这种情况下可能被传递给其他例程类型IEnumerable<T>
以及与其他程序将不知道该物体最终将需要配置,但是 。 相比之下,实例IEnumerator<T>
经常创建,使用和最终被放弃,通过一无所知,他们正在返回的基本类型超出了事实的情况下的代码IEnumerable<T>
的一些实施方式IEnumerable<T>.GetEnumerator()
的返回实现IEnumerator<T>
将泄漏资源,如果他们的IDisposable.Dispose
它们被遗弃之前不被调用的方法,并且它接受类型的参数最代码IEnumerable<T>
将没有知道,如果这种类型可以被传递给它的方式。 尽管这对于可能IEnumerable<T>
成包括属性EnumeratorTypeNeedsDisposal
以指示是否返回IEnumerator<T>
将必须被设置,或只要求其调用例程GetEnumerator()
检查返回的对象的类型,以查看是否它实现IDisposable
,它的速度更快,更容易无条件调用Dispose
,可能无法做任何事情的方法,而不是确定是否Dispose
是必要的,并称之为只有这样。
IDispoable是一个非常通用的接口,还有让你的界面从它继承没有坏处。 你会因此避免类型检查你的代码的唯一成本在你的一些ISample实现的无操作实现。 所以你的第二选择可能是从这个角度更好。
我开始以为把IDisposable
界面上会引起一些问题。 它意味着实现该接口的所有对象的生命周期可以安全地同步结束。 也就是说,它允许任何人这样写代码,并要求所有的实现支持IDisposable
:
using (ISample myInstance = GetISampleInstance())
{
myInstance.DoSomething();
}
仅其被访问的具体类型的代码可以知道,以控制对象的生命周期的正确方法。 例如,一种可能不需要处理摆在首位,这可能支持IDisposable
,或者它可能需要awaiting
你用它做了一些后异步清理过程(例如, 类似选项2在这里 )。
接口笔者无法预测实现类的所有未来可能的生命周期/范围管理的需求。 接口的目的是为了让一个对象暴露了一些API,以便它可以成为有用的一些消费者。 一些接口可能与生命周期管理(如IDisposable
本身),但与无关的生命周期管理接口混合它们可以使编写接口的实现很难或不可能。 如果你有你的接口很少实现和组织你的代码,使接口的消费者和寿命/范围经理在同一个方法,那么这种区别并不清楚在第一。 但是,如果你开始四处传递你的对象,这会更清楚。
void ConsumeSample(ISample sample)
{
// INCORRECT CODE!
// It is a developer mistake to write “using” in consumer code.
// “using” should only be used by the code that is managing the lifetime.
using (sample)
{
sample.DoSomething();
}
// CORRECT CODE
sample.DoSomething();
}
async Task ManageObjectLifetimesAsync()
{
SampleB sampleB = new SampleB();
using (SampleA sampleA = new SampleA())
{
DoSomething(sampleA);
DoSomething(sampleB);
DoSomething(sampleA);
}
DoSomething(sampleB);
// In the future you may have an implementation of ISample
// which requires a completely different type of lifetime
// management than IDisposable:
SampleC = new SampleC();
try
{
DoSomething(sampleC);
}
finally
{
sampleC.Complete();
await sampleC.Completion;
}
}
class SampleC : ISample
{
public void Complete();
public Task Completion { get; }
}
在上面的代码示例中,我展示了三种类型的生命周期管理方案,加入到你提供的两个。
-
SampleA
是IDisposable
与同步using () {}
支持。 -
SampleB
采用纯垃圾收集(它不消耗任何资源)。 -
SampleC
使用从被同步地设置其防止其资源,并且需要一个await
在其寿命结束时(使得它可以通知它完成消耗资源和冒泡任何异步遇到例外寿命管理代码)。
通过保持生命周期管理单独从其他接口,可以防止开发商错误(例如,意外调用Dispose()
)和更干净支持未来的意料之外的一生/范围管理模式。
个人而言,我会选择1,除非你做一个具体的例子有两个。 两个一个很好的例子是IList
。
一个IList
意味着你需要实现您的收藏一个索引。 然而, IList
真的也意味着你是一个IEnumerable
,你应该有一个GetEnumerator()
为你的类。
在你的情况是hesistant是实现类ISample
需要实现IDisposable
,如果不是每一个类,它实现你的接口必须实现IDisposable
那就不要强迫他们。
着眼于IDispoable
具体而言, IDispoable
尤其是迫使程序员使用你的类写一些相当丑陋的代码。 例如,
foreach(item in IEnumerable<ISample> items)
{
try
{
// Do stuff with item
}
finally
{
IDisposable amIDisposable = item as IDisposable;
if(amIDisposable != null)
amIDisposable.Dispose();
}
}
不仅是可怕的代码,会出现在确保有一个finally块来该列表的每次迭代处理的项目,即使显著的性能损失Dispose()
刚刚返回的执行情况。
粘贴代码来回答在这里评论的一个,更易于阅读。