我最近学会了如何使用Microsoft.VisualBasic.FileIO.TextFieldParser
解析文本输入。在给我的例子中,使用关键字using
调用TextFieldParser
using (var parser = new Microsoft.VisualBasic.FileIO.TextFieldParser(new StringReader(str)))
尽管经过进一步的研究,我注意到TextFieldParser
使用using
关键字的做法并不普遍。
据我所知,.Net
框架既有托管资源,也有非托管资源。当我们使用非托管资源时,我们应该担心内存泄漏,因此我们应该注意处理我们使用的非托管资源。一个最好的方法是将它们放在using
上下文中。
所有这些都驱使我在脑海中产生了两个问题,一个是特定的问题,另一个是一般的问题。以下是我的问题:
- 特别是:TextFieldParser实际上是托管的还是非托管的
- 一般情况:有没有一种明确的方法可以让我们知道资源是托管的还是非托管的(比如查看类中的
X
之类的东西,或者甚至从MSDN中检查一些东西(如果应该检查的话)。在我短暂的编程经验中,有人告诉我一些指导,比如(I)大多数.Net
类都是托管的,(ii)System.Drawing类有一些非托管资源,(iii)小心所有数据库、网络和COM类,因为它们通常是非托管的,等等。我一直在添加列表,直到现在。但我想知道是否有任何明确的方法可以知道这一点
如果更有经验的人能帮助我在这个问题上进一步指导我,我将不胜感激。
您没有抓住要点。每当任何类实现IDisposable
时,您都应该在完成后调用Dispose
该类是否使用非托管资源是该类的内部资源,您根本不应该关心它。每个使用非托管资源的类都应该有一个终结器,如果您没有显式处理该类,它可以清除这些非托管资源。Dispose
只允许您以更具确定性和即时性的方式清理其资源(包括托管和非托管资源,尽管这并不一定意味着立即释放内存)。例如,使用FileStream
的Dispose
将立即释放文件句柄,而如果不使用Dispose
(或Close
),则将打开该文件,直到下一次收集和最终确定为止。
编辑:
为了表明Dispose
对于清理托管资源可能也是必要的,我们只需要查看事件处理程序。特别是,当你订阅一个类的事件时,它的生存期比你长:
var control = new MyHelperControl();
MyParentForm.Click += control.DoSomething();
现在,即使control
超出范围,它也将与MyParentForm
一样长时间存在——它仍然被事件处理程序引用。当父应用程序与整个应用程序具有相同的生存期时,同样的问题会发展到荒谬的程度——这可能是一个巨大的内存泄漏。一个例子是在应用程序的主窗体或静态事件上注册事件处理程序。
在Dispose
中可能还会发生其他事情。例如,对于Windows窗体,当您在Control
上调用Dispose
时,会发生很多事情:
- 将释放所有非托管资源。由于Winforms控件在某种程度上是本机控件的包装器,因此这通常需要大量资源——控件本身、任何笔、画笔、图像。。。所有这些都是本地资源。如果你忘记了
Dispose
,它们也会被释放,因为它们都有终结器,但这可能需要更多的时间——当你创建和销毁很多这样的对象时,这尤其痛苦。例如,GDI+对象句柄的供应有限,如果用完了,就会得到OutOfMemoryException
- 处理所有子控件
- 删除任何上下文菜单处理程序(记住,上下文菜单是一个单独的组件,只链接到另一个控件)
- 删除任何数据绑定
- 从父容器中删除本身(如果有的话)
- 如果控件是一个具有自己的消息循环的窗口,则终止该消息循环
有趣的是,非托管资源实际上是最不重要的——它们总是有一个终结器。托管资源比较棘手,因为或多或少禁止在终结器中处理托管引用(因为它们可能已经发布,或者正在发布中,或者可能在终结器的中间开始发布…这很复杂)。因此,在终结器中执行MyParentForm.Click -= this.OnClick;
并不是一件好事,更不用说它需要您使每个这样的类都可终结,这并不是完全免费的,尤其是当您期望终结器实际运行时(当您执行Dispose
时,GC会被提醒此实例不再需要终结)。
任何类的使用implement IDisposable接口都应该使用(…){}包装,或者在适当的时候很好地处理。