处理.NET IDisposable对象

我在C#中工作,我一直在using块来声明实现IDisposable对象,这显然是你应该做的。 但是,当我滑倒的时候,我看不出一个简单的方法。 Visual Studio似乎没有以任何方式表明这一点(我只是错过了什么?)。 我每次宣布什么都应该检查一下帮助,逐渐build立一个百科全书式的记忆,其中的物品是不是一次性的? 看起来不必要,痛苦,容易出错。

你如何处理这个?

编辑:

看一下相关的问题边栏,我发现了另外一个问题 ,清楚地表明Dispose()应该被对象的终结器调用。 所以,即使你从来没有自己调用它,它应该最终发生,这意味着如果你不使用(这是我认为我一直很担心),你将不会有内存泄漏。 唯一需要注意的是,垃圾收集器不知道对象将多less额外的内存作为非托pipe的东西,所以它不会有一个准确的想法,多less内存将通过收集对象被释放。 这将导致垃圾收集器的性能比平时更差。

简而言之,如果我错过了一个using ,那不是世界末日。 我只是希望能够给它带来至less一个警告。

(题外话:为什么没有特殊的降价链接到另一个问题?)

编辑:

好吧,不要吵闹 这是超级duper所有发射戏剧性的花栗鼠级重要的称之为Dispose()或我们都会

现在。 因此,为什么这么容易 – 到底为什么它被允许 – 做错了? 你必须走出正确的道路。 像所有其他事情一样,会导致世界末日(显然)。 这么多封装,是吧?

[偷懒,厌恶]

FxCop 可能会帮助(虽然没有发现我刚刚开了一个testing); 但是:是的,你打算检查。 IDisposable是系统中这样一个重要的部分,你需要进入这个习惯。 使用intellisense寻找.D是一个好的开始(虽然不完美)。

但是,熟悉需要处理的types并不需要很长时间, 一般来说涉及任何外部的东西(连接,文件,数据库),例如。

ReSharper也做这个工作,提供一个“投入使用结构”选项。 虽然它不提供它作为错误

当然,如果你不确定 – 尝试 using它:如果你是偏执的,编译器会嘲笑你:

 using (int i = 5) {} Error 1 'int': type used in a using statement must be implicitly convertible to 'System.IDisposable' 

如果一个对象实现了IDisposable接口,那么它是有原因的,你打算叫它,它不应该被视为可选的。 最简单的方法是使用一个using块。

Dispose()并不打算只被对象的终结器调用,事实上,许多对象将实现Dispose()但没有终结器(这是完全有效的)。

处理模式背后的全部理念是,您提供了一种有些确定性的方式来释放由对象(或其inheritance链中的任何对象)维护的非托pipe资源。 通过不正确地调用Dispose() ,你绝对可以运行内存泄漏(或任何其他问题)。

Dispose()方法与析构函数没有任何关系。 在.NET中最接近析构函数的是终结器。 using语句不会执行任何释放操作…事实上,调用Dispose()不会对托pipe堆执行任何释放操作。 它只释放已分配的非托pipe资源。 在GC运行并收集分配给该对象图的内存空间之前,被pipe理的资源并不真正释放。

确定类是否实现IDisposable的最佳方法是:

  • 智能感知(如果它有一个Dispose()Close()方法)
  • MSDN
  • 反光
  • 编译器(如果它没有实现IDisposable你会得到一个编译器错误)
  • 常识(如果感觉你应该在完成后closures/释放对象,那么你可能应该
  • 语义(如果有一个Open() ,可能有一个对应的Close()应该被调用)
  • 编译器。 尝试将其置于using语句中。 如果它没有实现IDisposable,编译器将会产生一个错误。

把configuration模式看作是关于范围生命周期pipe理的一切。 你想尽可能地获得资源,尽可能快地使用,并尽快释放。 使用语句有助于确保即使在有例外的情况下也可以调用Dispose()

这就是为什么(恕我直言)C + +的RAII优于.NET的using声明。

很多人都说IDisposable只适用于不受pipe理的资源,这是唯一的依赖于你如何定义“资源”。 你可以有一个读/写locking实现IDisposable ,然后“资源”是对代码块的概念访问。 您可以有一个对象,将光标更改为构造函数中的小时,然后返回到IDispose先前保存的值,然后“资源”是更改后的光标。 我想说,当你想在离开范围时要使用确定性动作的时候,使用IDisposable,而不pipe范围如何,但是我不得不承认,这比说“pipe理不受pipe理的资源pipe理”要容易得多。

另请参阅为什么在.NET中没有RAII的问题。

简而言之,如果我错过了一个使用,那不是世界末日。 我只是希望能够给它带来至less一个警告。

这里的问题是你不能总是处理一个IDisposable只是包装在一个using块。 有时你需要这个物体再呆一会儿。 在这种情况下,你将不得不自己调用它的Dispose方法。

一个很好的例子就是一个类使用一个私有的EventWaitHandle(或一个AutoResetEvent)在两个线程之间进行通信,并且一旦线程完成,就想要抛弃WaitHandle。

所以它不像仅仅检查你在一个using块中创buildIDisposable对象的工具那么简单。

@Aario,不仅被接受的答案是错误的,你自己的编辑也是如此。 想象下面的情况( 实际发生在Visual Studio 2005的一个CTP中):

为了绘制graphics,您不用处理就可以创build笔。 钢笔不需要太多内存,但内部使用GDI +句柄。 如果您不处理笔,GDI +手柄将不会被释放。 如果您的应用程序不是内存密集型的,那么在没有调用GC的情况下,相当一段时间可以通过 但是,可用的GDI +句柄的数量受到限制,并且很快,当您尝试创build新笔时,操作将失败。

事实上,在Visual Studio 2005 CTP中,如果你使用的应用程序足够长,所有的字体会突然切换到“系统”。

这正是为什么依靠GC来处理是不够的 。 内存使用量不一定与您获得的非托pipe资源的数量相关(并且不会释放)。 因此,这些资源在GC召集之前可能已经用尽了。

另外,当然这些资源可能具有的副作用(如访问锁)的所有方面都会阻止其他应用程序正常工作。

不幸的是,FxCop或StyleCop似乎都没有提醒。 正如其他评论者所提到的那样,确保调用处置通常是相当重要的。 如果我不确定,我总是检查对象浏览器(Ctrl + Alt + J)来查看inheritance树。

我没有什么东西要添加到使用块的一般用途,但只是想添加一个例外的规则:

任何实现IDisposable的对象显然都不应该在其Dispose()方法中引发exception。 这完美的工作,直到WCF(可能有其他人),现在有可能在Dispose()期间WCF通道引发exception。 如果在Using块中使用它,则会导致问题,并需要执行exception处理。 这显然需要更多关于内部工作的知识,这就是为什么微软现在build议不要在Using块中使用WCF频道(抱歉找不到链接,但是在Google中有很多其他的结果),尽pipe它实现了IDisposable。复杂!

像Fxcop(他们是相关的),VS中的代码分析工具(如果你有更高级的版本)也会发现这些情况。

总是尝试使用“使用”块。 对于大多数对象来说,它并没有太大的区别,但是我遇到了一个最近的问题,那就是我在类中实现了一个ActiveX控件,除非正确调用Dispose,否则不能正常清理。 底线是即使它似乎没有太大的区别,尝试做正确的,因为一段时间它会有所作为。

我使用主要用于这种情况的块:

我正在使用一些外部对象(在我的情况下通常是一个IDisposable包装的COM对象)。 对象本身的状态可能会导致它抛出exception,或者如何影响我的代码,可能会导致我抛出一个exception,也许在许多不同的地方。 一般来说,我不信任我现有方法以外的代码来performance自己。

为了说明起见,我们可以说我有11个退出点,我的方法中有10个退出点,而其中的10个是在这个块内部的,后面是1个(这在我写的一些库代码中是典型的)。

由于该对象在退出使用块时会自动处理,所以我不需要进行10次不同的.Dispose()调用 – 只是发生了。 这样会产生更清晰的代码,因为它现在已经不像处理调用那样混乱了(在这种情况下,代码行数减less了10个)。

如果忘记调用dispose,那么通过修改代码后面的代码来引入IDisposable泄漏错误(这可能会耗费很长时间才能find)的风险较小,因为对于使用块而言不是必需的。

根据这个链接 CodeRush加载项将检测和标志当本地IDisposablevariables不清理,实时,当你键入。

可以在你的任务中半路见面。

我不明白你的问题。 由于垃圾收集器,内存泄漏几乎不可能发生。 但是,您需要一些强大的逻辑。

我用这个来创buildIDisposable类:

 public MyClass: IDisposable { private bool _disposed = false; //Destructor ~MyClass() { Dispose(false); } public void Dispose() { Dispose(true); } private void Dispose(bool disposing) { if (_disposed) return; GC.SuppressFinalize(this); /* actions to always perform */ if (disposing) { /* actions to be performed when Dispose() is called */ } _disposed=true; } 

现在,即使你错过了使用语句,对象也最终会被垃圾收集,并执行适当的销毁逻辑。 您可以停止线程,结束连接,保存数据,无论您需要什么(在本例中 ,我退订远程服务并根据需要执行远程删除调用)

[编辑]显然,尽快调用Dispose来帮助应用程序性能,这一个很好的做法。 但是,由于我的例子,如果你忘记调用Dispose,它将最终被调用,并清理对象。