在C#中需要手动清理的资源?

11

C#中有哪些资源需要手动清理?不进行清理会有什么后果?

例如,假设我有以下代码:

myBrush = new System.Drawing.SolidBrush(System.Drawing.Color.Black);
// Use Brush

如果我不使用 dispose 方法清除刷子,我是否假定垃圾回收器会在程序终止时释放使用的内存?这个说法正确吗?

还需要哪些资源来手动清理?

14个回答

7
  • 内部窗口数据结构的句柄。
  • 数据库连接。
  • 文件句柄。
  • 网络连接。
  • COM/OLE引用。

类似这样的内容还有很多。

调用Dispose方法非常重要,甚至更好的做法是使用using模式。

using (SolidBrush myBrush = new System.Drawing.SolidBrush(System.Drawing.Color.Black))
{
    // use myBrush
}

如果你不释放某个对象,它会在垃圾回收器注意到没有对它的引用时被清除,这可能需要一些时间。

对于System.Drawing.Brush,Windows会保留其内部窗口结构的加载直至所有程序都释放了其句柄。


7
如果您不对某个对象进行处理,那么当垃圾收集器注意到您的代码中没有对该对象的引用时,它将在一段时间后被清除。对于某些对象来说,这并不重要,但对于一个打开的文件来说可能很重要。
一般情况下,如果某个对象具有Dispose方法,则应在完成使用后调用它,或者如果可以的话,将其封装在using语句中:
using (SolidBrush myBrush = new System.Drawing.SolidBrush(System.Drawing.Color.Black))
{
    // use myBrush
}

3

不正确地处理IDisposable对象可能导致应用程序性能受损,甚至崩溃。

在您的示例中,Brush对象将在GC感觉合适时进行清理。但是,如果您提前清理它,您的程序将无法获得额外内存的好处。如果您正在使用大量Brush对象,则这可能变得重要。GC还更有效地清理对象,如果它们没有存在很长时间,因为它是一种分代垃圾回收器。

另一方面,不正确地处理数据库连接对象可能意味着您很快就会耗尽池化数据库连接并导致应用程序崩溃。

要么使用

using (new DisposableThing...
{
    ...
}

或者,如果您需要在对象的生命周期内保留对IDisposable的引用,请在对象上实现IDisposable并调用IDisposable的Dispose方法。

class MyClass : IDisposable
{
    private IDisposable disposableThing;

    public void DoStuffThatRequiresHavingAReferenceToDisposableThing() { ... }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    //etc... (see IDisposable on msdn)

}


2

通常,任何实现IDisposable接口的内容都应该让你停下来,研究一下你正在使用的资源。

垃圾回收只会在有内存压力时发生,因此您无法预测什么时候会发生。虽然卸载AppDomain肯定会触发它。


1

从技术上讲,任何继承自IDisposable的东西都应该被主动处理。您可以使用'using'语句使事情变得更容易。

http://msdn.microsoft.com/en-us/library/yh598w02.aspx

有时候你会在文档示例代码以及工具生成的代码(比如 Visual Studio)中看到 IDisposable 派生对象的不一致使用。
IDisposable 的好处在于它让你有能力主动释放底层的非托管资源。有时候你真的想这样做——比如网络连接和文件资源。

1

正如其他人所说,使用using语句是你的好朋友。 我写了这篇博客文章,介绍了如何以相当简单的方式实现IDisposable接口,通过将最重要的部分分解出来,从而减少出错的可能性。


1
当我无法记住一个给定对象是否为可处理资源时,我使用的技巧是在声明后键入“.Dispose”(最多!),以便Intellisense为我检查:
MemoryStream ms = new MemoryStream().Dispose

然后删除 .Dispose 并使用 using() 指令:
using(MemoryStream ms = new MemoryStream())
{
  ...
}

请注意,如果已经显式实现了Dispose,则此技巧将无效! - Rasmus Faber

0
与其认为对象“持有”需要释放的资源,不如将对象视为已经改变了某些东西(可能在计算机之外!),这些改变将会比较危险,如果不撤销或“清理”的话。但是只有对象才能进行清理。虽然这种改变通常采用将池中的某个具体对象标记为“繁忙”的形式,但其确切形式并不重要。重要的是需要撤销这些更改,并且对象保存了必要的信息来完成此操作。

0

只要您使用托管版本的资源并且不自己调用Windows API,那么就应该没问题。唯一需要担心的是当您获得一个IntPtr时需要删除/销毁资源,因为在.NET中,“Windows句柄”(以及许多其他内容)是已知的,而不是对象。

顺便说一下,资源(像任何其他.NET对象一样)将在您离开当前上下文时被标记为可回收,因此如果您在方法内部创建Brush,则在退出方法时它将被标记。


0

如果它是托管的(即框架的一部分),您不需要担心它。如果它实现了IDisposable接口,只需将其包装在using块中即可。

如果您想使用非托管资源,则需要了解有关终结器和自己实现IDisposable的更多信息。

有关更多详细信息,请参见此问题


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