确定 IDisposable 应该扩展接口还是被实现在实现该接口的类中

29
我该如何确定是否应该在我的接口上扩展IDisposable,还是在实现我的接口的类上实现IDisposable?
我有一个不需要处理任何外部资源的接口,除了其中一个特定的实现。我的选择似乎是:
1) 在要求所有实现都实现Dispose(即使只是空方法)的接口上实现IDisposable。
-或-
2) 仅在具有需要处理的资源的类上实现IDisposable。这会在使用时造成问题,因为我的对象是从工厂创建的,因此所有上游代码都针对接口工作。由于接口没有绑定到IDisposable,因此“using”看不到Dispose方法。不过,我可以将工厂结果转换为实现;但这会使使用者意识到实现,从而破坏了接口的目的。
有没有关于最佳实践的建议?

3
如果您希望您的接口支持“using”,那么第一种选项是唯一的选择。毕竟,所有的代码都将针对接口本身编写,而不是针对特定的实现。 - Federico Berasategui
一个“Dispose”的空实现可能是多余的,但它在第二个中被实现。 - faester
3个回答

22
如果您希望调用者只能与接口交互,而不能与实现交互,则需要使接口扩展IDisposable。否则,他们仍然需要检查value is IDisposable以查看是否需要处理它。
如果负责处理对象的对象知道具体实现,并且仅有获得其引用但不负责处理它的对象使用该接口,则考虑第二个选项。
第一个选项的典型例子是IEnumerator。许多IEnumerator对象在被处理时不需要做任何事情,但有些对象需要,因此该接口扩展了IDisposable,因为负责创建/生命周期的对象将(或应该)从未知道底层实现。
第二个例子是IComparer,许多需要进行比较的对象都是可处理的,但使用接口的代码部分不负责创建/生命周期,因此不需要了解该类型是否可处理。

11

这个问题的关键在于:是否有可能将处理责任随接口传递,或者换句话说,最后一个使用实现对象的实体可能不是创建它的实体。

IEnumerator<T> 实现 IDisposable 的主要原因在于,实现对象是由实现 IEnumerable<T>.GetEnumerator() 接口的对象创建的,但通常会被其他对象使用。实现 IEnumerable<T> 的对象将知道它返回的东西是否需要清除,但没有办法知道接收方何时完成使用它。调用 IEnumerable<T>.GetEnumerator() 的代码将知道何时完成对返回的对象的使用,但无法知道它是否需要任何清理。明智的做法是指定调用 IEnumerable<T>.GetEnumerator() 的代码必须确保在放弃该对象之前调用 Dispose 方法,Dispose 方法在许多情况下不会执行任何操作,但无条件地调用一个保证存在的空操作方法比检查不存在的方法要便宜。

如果接口类型的性质使得问答实现对象是否需要清理和何时进行此类清理都可以由同一实体回答,则无需继承 IDisposable 接口。如果实例将由一个实体创建,但由另一个实体最后使用,则继承 IDisposable 接口是明智的。


6
如果在具体类和接口上实现IDisposable,并且接口使用者知道它可能是可处理的,则可以执行以下操作。
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();
    }

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接