如何确定是否应该使用IDisposable扩展我的一个接口,或者在实现我的接口的类上实现IDisposable?
我有一个不需要处理任何外部资源的接口,除了一个特定的实现。我的选择似乎是:
1)在接口上实现IDisposable,要求所有实现都实现Dispose,即使只是一个空方法。
-OR -
2)仅在需要处理资源的类上实现IDisposable。这将导致“使用”问题,因为我的对象是从工厂创建的,因此所有上游代码都对接口起作用。由于接口未绑定到IDisposable,因此“使用”不会看到Dispose方法。但是,我可以将工厂结果转换为实施;然而,这会使消费者意识到实施,从而破坏了接口的目的。
关于最佳做法的任何想法?
答案 0 :(得分:13)
如果您希望调用者只能与接口交互,而不是实现,那么您希望接口扩展IDisposable
。如果没有,他们无论如何都需要检查value is IDisposable
是否需要处理。
如果负责处理对象的对象知道具体的实现,并且只有对象使用接口给它(但不负责处理它),那么考虑第二个选项。
第一个选项的一个很好的例子是IEnumerator
。许多IEnumerator
个对象在处理时不需要做任何事情,但有些对象做了,因此接口扩展IDisposable
,因为负责该对象的创建/生命周期的对象将(或应该) )从不了解底层实施。
第二个例子就像IComparer
这些需要比较的对象是一次性的,但是通过接口使用对象的代码部分不负责它是创造/生命周期,所以它不需要知道这种类型是否是一次性的。
答案 1 :(得分:7)
50,000美元的问题是处置的责任是否会与接口一起传递,或者换句话说,使用实现对象的最后一个实体是否可能是创建它的实体以外的其他实体。
IEnumerator<T>
实现IDisposable
的一个重要原因是实现对象是由实现IEnumerable<T>.GetEnumerator()
但通常由其他对象使用的对象创建的。实现IEnumerable<T>
的对象将知道它返回的东西是否真的需要处理,但是无法知道收件人何时完成它。调用IEnumerable<T>.GetEnumerator()
的代码将知道何时完成返回的对象,但无法知道是否需要进行任何清理。明智的做法是指定调用IEnumerable<T>.GetEnumerator()
的代码是必需的,以确保返回的对象在被放弃之前调用它Dispose
;在许多情况下,Dispose
方法不会做任何事情,但无条件地调用保证存在的无操作方法比检查不存在的方法的存在要便宜。
如果接口类型的性质使得实现对象是否需要清理以及何时应该进行清理的问题都将由同一实体负责,那么接口就不需要继承{{1 }}。如果实例将由一个实体创建但最后由另一个实体使用,则继承IDisposable
将是明智的。
答案 2 :(得分:4)
如果在具体类上实现IDisposable
,并且接口用户知道它可能是Disposable;你可以做到
IFoo foo = Factory.Create("type");
using(foo as IDisposable){
foo.bar();
}
如果foo
未实现IDisposable
,则using
将达到using(null)
,这将正常工作。
以下示例程序的输出将是
Fizz.Bar
Fizz.Dispose
Buzz.Bar
示例程序
using System;
internal interface IFoo
{
void Bar();
}
internal class Fizz : IFoo, IDisposable
{
public void Dispose()
{
Console.WriteLine("Fizz.Dispose");
}
public void Bar()
{
Console.WriteLine("Fizz.Bar");
}
}
internal class Buzz : IFoo
{
public void Bar()
{
Console.WriteLine("Buzz.Bar");
}
}
internal static class Factory
{
public static IFoo Create(string type)
{
switch (type)
{
case "fizz":
return new Fizz();
case "buzz":
return new Buzz();
}
return null;
}
}
public class Program
{
public static void Main(string[] args)
{
IFoo fizz = Factory.Create("fizz");
IFoo buzz = Factory.Create("buzz");
using (fizz as IDisposable)
{
fizz.Bar();
}
using (buzz as IDisposable)
{
buzz.Bar();
}
Console.ReadLine();
}
}