在没有资源的类上实现IDisposable有什么好处吗?

时间:2012-02-27 21:07:57

标签: c# idisposable

在C#中,如果某个类(如经理类)没有资源,那么使用: IDisposable是否有任何好处?

简单示例:

public interface IBoxManager
{
 int addBox(Box b);
}

public class BoxManager : IBoxManager
{
 public int addBox(Box b)
 {
  using(dataContext db = new dataContext()){
   db.Boxes.add(b);
   db.SaveChanges();
  }
  return b.id;
 }
}

使用BoxManager时,如果它还实现了IDisposable,那么内存使用会有什么好处吗? public class BoxManager : IBoxManager , IDisposable

例如:

BoxManager bm = new BoxManager();
bm.add(myBox);
bm.dispose();//is there benefit to doing this?

11 个答案:

答案 0 :(得分:72)

在类型

上实施IDisposable只有两个原因
  • 该类型包含必须在不再使用该类型时释放的本机资源
  • 该类型包含IDisposable
  • 类型的字段

如果这些都不是真的那么就不要实施IDisposable

编辑

有些人提到IDisposable是实现开始/结束或预订操作的好方法。虽然这不是IDisposable的原始意图,但确实提供了非常好的模式。

class Operation {
  class Helper : IDisposable {
    internal Operation Operation;
    public void Dispose() {
      Operation.EndOperation();
    }
  }
  public IDisposable BeginOperation() {
    ...
    return new Helper() { Operation = this };
  }
  private void EndOperation() {
    ...
  }
}

注意:实现此模式的另一种有趣方法是使用lambdas。而不是向用户提供IDisposable并希望他们不要忘记调用Dispose让他们给你一个lambda,他们可以在其中执行操作并关闭操作

public void BeginOperation(Action action) {
  BeginOperationCore();
  try {
    action();
  } finally {
    EndOperation();
  }
}

答案 1 :(得分:7)

如果您没有明确使用Dispose()方法,则一次性和非一次性版本之间不会有任何差异。

答案 2 :(得分:6)

虽然您的代码不会从实现IDisposable中受益,但我不同意其他意见,其中声明IDisposable仅用于(直接或间接)免费的本机资源。只要对象需要在其生命周期结束时执行清理任务,就可以使用IDisposable。它与using一起非常有用。

一个非常流行的例子:在ASP.NET MVC Html.BeginForm中返回一个IDisposable。在创建时,对象打开标记,当调用Dispose时它会关闭它。没有涉及本地资源,但仍然很好地使用IDisposable。

答案 3 :(得分:5)

不,如果您没有做一些有用的事情,例如释放您的类可能在Dispose方法中保存的非托管资源,那将没有任何好处。

答案 4 :(得分:4)

不,如果没有(托管或非托管)资源,则也不需要IDisposable

小警告:有些人使用IDisposable来清理事件处理程序或大内存缓冲区,但

  • 你似乎没有使用那些
  • 无论如何,这是一个有问题的模式。

答案 5 :(得分:4)

一个主要的混淆点,可能不适用于您的情况,但经常出现,恰恰构成了“资源”。从对象的角度来看,非托管资源是外部实体()代表它“执行”( *)的东西,外部实体将继续这样做 - 这对其他实体不利有权利 - 直到被告知停止。例如,如果一个对象打开一个文件,那么托管该文件的机器可以授予该对象独占访问权,拒绝宇宙中的其他人有机会使用它,除非或者直到它被告知不再需要独占访问。

(*)可以是任何地方,任何地方;甚至可能不在同一台电脑上。

(**)或改变外部实体的行为或状态的某种方式

如果外部实体代表被抛弃的对象做某事并且在没有首先让实体知道其服务不再需要的情况下消失,那么外部实体将无法知道它应该停止代表不再存在的对象。 IDisposable通过提供在不需要其服务时通知对象的标准方法,提供了一种避免此问题的方法。不再需要服务的对象通常不需要向任何其他实体提出任何进一步的好处,因此可以请求代表其行事的任何实体应该停止这样做。

为了允许在没有首先调用IDisposable.Dispose()的情况下放弃对象的情况,系统允许对象注册名为Finalize()的“故障安全”清理方法。因为无论出于何种原因,C#的创建者不喜欢术语Finalize(),该语言需要使用称为“析构函数”的构造,它具有相同的功能。请注意,一般情况下,Finalize()会掩盖而不是解决问题,并且可能会产生问题,因此如果有的话,应该非常谨慎地使用它。

“托管资源”通常是为实现IDisposable的对象赋予的名称,通常但并不总是实现终结器。

答案 6 :(得分:3)

根据我的个人经验(通过讨论和其他帖子确认)我会说,可能存在这样的情况:您的对象使用了大量的事件,或者没有大量但是经常订阅和取消订阅事件有时会导致对象不是垃圾收集。在这种情况下,Dispose中的 I 取消订阅我之前订阅的所有事件。

希望这有帮助。

答案 7 :(得分:3)

如果您想使IDisposable语法受益,

using () {}也很棒。

在使用ViewModels的WPF项目中,我希望能够暂时禁用NotifyPropertyChange个事件。为了确保其他开发人员能够重新启用通知,我编写了一些代码来编写类似的内容:

using (this.PreventNotifyPropertyChanges()) {
    // in this block NotifyPropertyChanged won't be called when changing a property value
}

语法看起来不错,易于阅读。要使它工作,需要编写一些代码。您将需要一个简单的Disposable对象和计数器。

public class MyViewModel {
    private volatile int notifyPropertylocks = 0; // number of locks

    protected void NotifyPropertyChanged(string propertyName) {
        if (this.notifyPropertylocks == 0) { // check the counter
            this.NotifyPropertyChanged(...);
        }
    }

    protected IDisposable PreventNotifyPropertyChanges() {
        return new PropertyChangeLock(this);
    }

    public class PropertyChangeLock : IDisposable {
        MyViewModel vm;

        // creating this object will increment the lock counter
        public PropertyChangeLock(MyViewModel vm) {
            this.vm = vm;
            this.vm.notifyPropertylocks += 1;
        }

        // disposing this object will decrement the lock counter
        public void Dispose() {
            if (this.vm != null) {
                this.vm.notifyPropertylocks -= 1;
                this.vm = null;
            }
        }
    }
}

这里没有资源可供处理。我想要一个带有一种try / finally语法的干净代码。 using关键字看起来更好。

答案 8 :(得分:2)

  

拥有它有什么好处:IDisposable?

在您的具体示例中看起来并不是这样,然而:即使您没有任何IDisposable字段,也有一个很好的理由来实施IDisposable:你的后代可能会。

这是IDisposable: What your mother never told you about resource deallocation中突出显示的IDisposable的一个重大架构问题。基本上,除非你的课程被密封,否则你需要决定你的后代是否有IDisposable个成员。这不是你能够实际预测的东西。

因此,如果您的后代可能有IDisposable个成员,那么这可能是在基类中实现IDisposable的一个很好的理由。

答案 9 :(得分:1)

简短的回答是否定的。但是,您可以巧妙地在对象生命周期结束时使用Dispose()执行的本质。一个人已经给出了一个很好的MVC示例(Html.BeginForm

我想指出IDisposableusing() {}陈述的一个重要方面。在Using语句结束时Dispose()方法会自动在使用上下文对象上调用(当然,它必须已经实现了IDisposable接口。)

答案 10 :(得分:0)

还有一个原因,没有人提到(虽然它是否值得辩论,但它是有争议的): 对象表示,如果有人使用实现IDisposable的类,则必须调用其Dispose方法(显式或通过'using'语句)。 但是如果一个类的V1(在你的公共API中)不需要IDisposable会发生什么呢,但V2呢?从技术上讲,它不会破坏向后兼容性以向类添加接口,但由于 convension ,它就是!因为代码的旧客户端不会调用其Dispose方法,并且可能导致资源无法释放。 (几乎)避免它的唯一方法是在任何情况下实施IDisposable,你怀疑将来需要它,以确保你的客户总是调用你的Dispose方法,有一天可能真的需要它。 另一种(可能更好的)方法是在API的V2中实现上面JaredPar提到的lambda模式。