【问题标题】:What counts as an "Unmanaged Resource" in the Disposable pattern?在 Disposable 模式中什么算作“非托管资源”?
【发布时间】:2012-03-31 04:25:55
【问题描述】:

我正在使用此处描述的模式来管理处置: http://www.developerzen.com/2006/01/09/finalizableobject-developing-a-base-class-for-idisposable-objects/

什么算作“非托管资源”?

以下是我心中的一些困惑点:

  • 在某些时候,.NET 只是包装 Win32 调用,对吗?那么大多数.NET 对象在某种程度上不是非托管资源吗?
  • 对于我们有 .NET 包装器的 COM 对象,它们是什么考虑的?
  • 那些仅从 P/Invokes 获得功能的托管类呢?
  • 内部使用本机库的 C++/CLI 类呢?在 C# 级别,具有析构函数的 C++/CLI 类现在实现了 IDisposable...它们被认为是什么?

我意识到那篇博文有点老了。如果有更现代的方法来管理非托管和托管对象的生命周期,请提出建议。

编辑:投票结束此问题的人,请提供一些有关我如何改进问题的详细信息。

【问题讨论】:

  • “如果有解决这个问题的新方法……” 究竟是什么问题?
  • 管理非托管和托管对象的生命周期。如果您以错误的顺序执行此操作,您可能会出现死锁
  • the documentation中给出的例子有什么问题?
  • @CodyGray,你的问题是什么?他要求澄清他的想法。
  • @Akash:我也在要求澄清。我不明白他要解决的问题是什么。我什么时候说过或表明我对这个问题有疑问?

标签: .net


【解决方案1】:

我理解这种困惑。我认为有一些简单的指导方针可以遵循:

  1. 如果您的类“拥有”对另一个对象的引用(即,您的对象创建了另一个对象,或者在理解您的对象现在“拥有”它的情况下获得了对该对象的引用),那么您就是负责进行任何必要的清理(如果有)

  2. 如果该其他对象是 .NET 对象并且未实现 IDisposable 接口,那么您可能不需要执行任何操作。按照惯例,IDisposable 接口是我们用来声明对象是否需要清理的接口。

  3. 如果其他对象是 .NET 对象并实现了 IDisposable 接口,那么您的类也应该实现 IDisposable 接口。在您的 Dispose 方法中,只需调用另一个对象的 Dispose 方法即可。

  4. 如果该其他对象是非托管对象或资源,则您的类应实现终结器以确保清理非托管对象/资源。请遵循该博客文章中描述的指南,了解如何实施。

以下是对您问题的一些具体回答。

  • 在某些时候,.NET 只是包装 Win32 调用,对吗?那么,在某种程度上,大多数 .NET 对象不是非托管资源吗?

尽管 .NET 对象可以包装 Win32 调用,但术语“非托管资源”并不合适,因为它是一个 .NET 对象,因此根据定义它是“托管资源”。 CLR 处理这些对象的内存分配和垃圾回收。

  • 那些我们有 .NET 包装器的 COM 对象呢?它们被认为是什么?

.NET 包装对象仍然是 .NET 对象,因此它们是“托管资源”,但请注意,它们必须实现自己的处置/终结逻辑。基本上,他们负责清理“非托管资源”,这样您就不必这样做了。如果您正在为 COM 对象实现自己的包装器,那么您将负责实现必要的处置/完成逻辑来清理它,这样您的包装器的使用者就不必这样做了。

  • 那些仅从 P/Invokes 获得功能的托管类呢?

使用 P/Invoke 调用非托管代码的托管类可能正在分配非托管资源,具体取决于它所调用的内容。因此,它可能有必要实现必要的 dispose/finalize 逻辑来清理那些非托管资源。

  • 内部使用本机库的 C++/CLI 类呢?

是的,它们也可能从本机库分配非托管资源,因此它们也有必要实现必要的析构函数/终结器逻辑。

  • 在 C# 级别,具有析构函数的 C++/CLI 类现在实现了 IDisposable...他们认为是什么?

在 C++/CLI 中,您可以选择将类声明为托管(使用 ref 关键字)或非托管(不使用 ref 关键字)。如果它是用 ref 关键字声明的,那么它是一个托管类;它将在托管堆上分配,一旦超出范围并且不再有对它的引用,就会被垃圾收集器清理。如果类声明时没有使用 ref 关键字,则它是非托管的,必须明确清除。在任何一种情况下,类都可能分配了需要在析构函数/终结器中清理的非托管资源。

【讨论】:

  • 非常感谢您解决问题 -- 这真的为我理清了概念。
【解决方案2】:

这是我视为“非托管”的内容(注意引号):

  1. 任何实现 IDisposable 的东西。如果它想被处置……好吧,相信它。它是纯“安全”.NET 还是实现空的 Dispose 方法都没有关系。为了使用 Disposable 模式处理它,它是“非托管的”。
  2. 任何 COM RCW。对此有很大的争论; I view each COM->NET RCW count-increase as "owned" and thus "unmanaged"最终 RCW 将在析构函数中自行清理,但“最终”在处理 Outlook 对象模型时可能会导致很多问题,而这正是我所使用的。这种方法需要一致的方法来处理 COM 对象和生命周期,以避免“RCW 分离”问题。
  3. 通过 P/Invoke 调用(例如 BSTR)获得的任何 [新] 资源:分配所述资源的事物“拥有”它,直到放弃控制权。这确实是一个“非托管”资源,必须释放。对于可以分配的各种资源,有一些有用的包装器类型。

我不处理非 COM 原生代码(例如 CLI),除了少数 WinAPI 调用,它们都是 C。

【讨论】:

    【解决方案3】:

    乍一看,.net 之外的任何东西都是非托管资源。托管对象是由它们组成的.net字符串、数组、值类型和复杂类型,它们都是托管的,它们的生命周期由.net自动控制。 String 不是围绕某物的包装器,因此它是完全托管的。

    大多数 .net 基类都是从头开始编写的,例如 List、Stack 等。我们可以通过说任何对象及其后代仅引用堆栈或托管堆中的数据来简化。

    任何其他打开的用于访问堆栈/堆之外的任何内容的连接/句柄都是不受管理的,无论是文件、命名管道、网络还是任何设备。

    大多数其他 BCL 类都经过优化,但除非您使用块将它们包装起来,否则它们不会以任何编写方式自动释放。

    【讨论】:

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