27

如何确定是否应该使用 IDisposable 扩展我的接口之一或在实现我的接口的类上实现 IDisposable?

我有一个不需要处理任何外部资源的接口,除了一个特定的实现。我的选择似乎是:

1) 在需要所有实现都实现 Dispose 的接口上实现 IDisposable,即使只是一个空方法。

-或者-

2) 仅在需要释放资源的类上实现 IDisposable。这将导致“使用”问题,因为我的对象是从工厂创建的,因此所有上游代码都对接口起作用。由于接口未绑定到 IDisposable,因此“使用”看不到 Dispose 方法。但是,我可以将工厂结果转换为实现;但是,这会使消费者意识到实现,从而破坏了接口的目的。

关于最佳实践的任何想法?

4

3 回答 3

19

如果您希望调用者只能与接口交互,而不能与实现交互,那么您希望接口扩展IDisposable。如果没有,他们将需要检查是否value is IDisposable仍然需要处理它。

如果负责处理对象的对象知道具体的实现,并且只有对象被赋予了对它的引用(但不负责处理它)才使用接口,那么考虑第二个选项。

第一个选项的一个很好的例子是IEnumerator. 许多IEnumerator对象在释放时不需要做任何事情,但有些对象需要做任何事情,因此接口扩展IDisposable,因为负责该对象的创建/生命周期的对象将(或应该)永远不知道底层实现。

第二种的一个例子是IComparer 很多需要比较的对象都是一次性的,但是通过接口使用对象的代码部分不负责它的创建/生命周期,所以它不需要知道是否类型是一次性的。

于 2013-01-16T22:07:21.100 回答
10

50,000 美元的问题是处置责任是否会与接口一起传递,或者换句话说,使用实现对象的最后一个实体是否可能不是创建它的实体。

实现的一个重要原因是IEnumerator<T>实现IDisposable对象是由实现的对象创建的,IEnumerable<T>.GetEnumerator()但随后通常被其他对象使用。实现的对象IEnumerable<T>会知道它返回的东西是否真的需要处理,但无法知道接收者什么时候处理完。调用的代码IEnumerable<T>.GetEnumerator()将知道何时处理完返回的对象,但无法知道它是否需要任何清理。明智的做法是指定调用的代码,IEnumerable<T>.GetEnumerator()以确保返回的对象Dispose在被放弃之前已经调用过它;这Dispose方法在很多情况下什么都不做,但是无条件地调用一个保证存在的不做任何事情的方法比检查一个不存在的方法是否存在要便宜。

如果您的接口类型的性质是实现对象是否需要清理以及何时应该进行清理的问题都将由同一个实体来回答,那么接口就不需要继承IDisposable。如果实例将由一个实体创建但最后由另一个实体使用,那么继承IDisposable将是明智的。

于 2013-01-18T00:20:29.833 回答
4

如果您IDisposable在具体类上实现并且接口用户知道它可能是一次性的;你可以做

IFoo foo = Factory.Create("type");
using(foo as IDisposable){
    foo.bar();
}

如果foo不执行IDisposableusing将达的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();
    }

}
于 2013-02-22T19:21:15.123 回答