【问题标题】:How do we distinguish between managed and unmanaged resources in C#? Is TextFieldParser unmanaged?我们如何区分 C# 中的托管资源和非托管资源? TextFieldParser 是非托管的吗?
【发布时间】:2016-01-05 09:59:11
【问题描述】:

我recently learned 如何使用Microsoft.VisualBasic.FileIO.TextFieldParser 解析文本输入。在给我的示例中,TextFieldParser 是使用关键字 using 调用的

using (var parser = new Microsoft.VisualBasic.FileIO.TextFieldParser(new StringReader(str)))

虽然在some further researches 之后,我注意到在TextFieldParser 中使用using 关键字的做法并不普遍。

据我了解,.Net Framework 既有托管资源也有非托管资源。当我们使用非托管资源时,我们应该worry about内存泄漏,因此我们应该处理我们使用的非托管资源。一种最好的方法是将它们放在using 上下文中。

所有这些都让我想到两个问题,一个是特殊的,一个是一般的。以下是我的问题:

  1. 特别之处:TextFieldParser 是托管的还是非托管的?
  2. 一般:是否有一种确定的方式让我们知道资源是托管还是非托管(例如查看类中的X 事物等,甚至检查某些东西来自MSDN - 如果需要检查 - 可以)。在我短暂的编程经验中,有人告诉我一些指导,例如 (i) 大多数 .Net 类是托管的,(ii) System.Drawing 类有一些非托管资源,(iii) 提防所有数据库、网络和 COM类,因为它们通常是非托管的,等等......我一直在添加到现在的列表。但我想知道是否有任何确定方法可以知道这一点?

如果有经验的人能在这个问题上帮助我进一步指导我,我将不胜感激。

【问题讨论】:

    标签: c# .net unmanaged managed textfieldparser


    【解决方案1】:

    你没有抓住重点。每当任何类实现IDisposable 时,您都应该在完成后调用Dispose。

    类是否使用非托管资源是类内部的,你根本不应该关心它。每个使用非托管资源的类还应该有一个终结器,如果您没有显式处置该类,则该终结器可以清除这些非托管资源。 Dispose 只允许您以更确定和立即的方式清理其资源(托管和非托管,尽管这并不一定意味着立即释放内存)。例如,DisposeFileStream 将立即释放文件句柄,而如果你不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 会收到警报,该实例不再需要终结)。

    【讨论】:

    • 您介意进一步详细说明您的解释吗?这是否意味着任何带有IDisposable 的类都是非托管的,而任何没有IDisposable 的类都是托管的?
    • @Ian 不,我的意思是类是否拥有非托管资源无关紧要。除非这个类写得很糟糕,否则即使你不调用Dispose 方法,它最终也会释放它的非托管资源。另一方面,Dispose 方法对于托管资源也可能是完全必需的 - 典型的例子是通过事件处理程序的内存泄漏。
    • @Ian:大多数实现IDisposable 的类不会直接拥有非托管资源。但是,他们通常会拥有另一个实现IDisposable 的类。沿着这条链走下去,大部分时间都会以非托管资源结束。一个例子:YourClass 有一个 FileStream 字段。 FileStream 类具有 SafeFileHandle 字段。 SafeFileHandle 拥有底层操作系统非托管文件句柄。处置 YourClass 应该处置 FileStream 处置 SafeFileHandle 将释放非托管文件句柄。
    • @Ian 尤其是查看TextFieldParser 的源代码,它确实非常适合这一点——Dispose 方法处理了该类拥有的其他IDisposables。虽然它有一个终结器,但它实际上并没有直接拥有任何非托管资源 - 大概是出于扩展原因(虚拟 Dispose 方法需要它)。所以调用Dispose 意味着文本流更早关闭,并且您避免了拥有可终结类的开销。但不使用Dispose 不会泄露任何东西——只是延迟一些清理工作。
    • 我试图理解你在说什么:你是说:(1)我们不知道一个类是否使用unmanaged类,但只要它实现IDisposable我们可以更确定地释放它的资源 (2) 即使一个资源是managed,但如果没有正确调用Dispose,并不意味着它不会导致内存泄漏。因此,IDisposable 的实现只是一个优秀的程序员在知道他/她的类占用大量资源的情况下对其类所做的方式。我理解正确吗?
    【解决方案2】:

    任何实现 IDisposable 接口的类的使用都应使用 (...) {} 包装或在适当的地方妥善处理。

    【讨论】:

      猜你喜欢
      • 2013-02-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-26
      • 2015-05-27
      • 1970-01-01
      相关资源
      最近更新 更多