我正在努力确保我对 IDisposable 的理解是正确的,并且我仍然不太确定。

IDisposable 似乎有两个目的。

  1. 提供“关闭”的约定。按需管理的对象。
  2. 提供免费“非托管资源”的约定。由管理对象持有。
  3. 我的困惑来自于确定哪些方案具有“非托管资源”。在游戏中。

    假设您使用的是Microsoft提供的 IDisposable - 实现(托管)类(例如,数据库或套接字相关)。

    1. 您如何知道是否正在为 1 1& 2 实施 IDisposable
    2. 您是否有责任确保释放内部可能会或可能不会保留的非托管资源?你应该在你自己的类中添加一个终结器(这是正确的机制吗?)来调用instanceOfMsSuppliedClass.Dispose()吗?
有帮助吗?

解决方案

  
      
  1. 你怎么知道它是否只为1或者实现IDisposable   1& 2以上?
  2.   

您的第一个问题的答案是“您不应该知道”。如果你正在使用第三方代码,那么你在某种程度上是它的怜悯 - 当你在它上面调用Dispose时,你必须相信它正在自行处理。如果您不确定或者您认为存在错误,您可以尝试使用Reflector()来反汇编它(如果可能的话)并查看它正在做什么。

  
      
  1. 我是否有责任确保可能会或可能会使用非托管资源   内部没有被释放?应该   我要添加一个终结器(就是这样   正确的机制?)到我自己的班级   那叫   instanceOfMsSuppliedClass.Dispose()?
  2.   

如果你使用.Net 2.0或更高版本,你应该很少(如果有的话)需要为你的类实现终结器。终结器为您的类增加了开销,并且通常只提供实现Dispose所需的功能。我会强烈建议您访问本文,以获得有关正确处理的良好概述。在您的情况下,您可能希望在自己的Dispose()方法中调用 instanceofMSSuppliedClass.Dispose()

最终,在对象上调用Dispose()是一种很好的做法,因为它明确地让GC知道你已经完成了资源并允许用户立即清理它,并通过让间接来间接记录代码其他程序员都知道该对象是在那时使用资源完成的。但即使您忘记明确地调用它,最终也会在对象无根时发生(毕竟.Net是托管平台)。只有当你的对象具有需要隐式清理的非托管资源时才应该实现终结器(即,消费者有可能忘记清理它,并且这将是有问题的)。

其他提示

你应该总是在实现IDisposable的对象上调用Dispose(除非他们明确告诉你'它是一个有用的约定,如ASP.NET MVC的HtmlHelper.BeginForm)。您可以使用“使用”声明使这很容易。如果您将类中IDisposable的引用作为成员字段挂起,则应使用一次性模式来清理这些成员。如果您运行像FxCop这样的静态分析工具,它会告诉您相同的内容。

你不应该试图猜测界面。今天该类可能不使用非托管资源,但下一个版本呢?

您不对对象的内容负责。 Dispose()应该是透明的,并且释放它需要释放的东西。在那之后,你不负责。

非托管资源就像您在(托管)C ++中创建的资源一样,您可以通过指针和“新”来分配内存。陈述,而不是“gcnew”声明。当你在C ++中创建一个类时,你负责删除这个内存,因为它是本机内存,或者是非托管内存,而垃圾收集器并不关心它。您还可以通过Marshal分配创建此非托管内存,并且我认为是不安全的代码。

使用Managed C ++时,您不必手动实现IDisposable类。编写解构函数时,它将被编译为Dispose()函数。

如果有问题的类是Microsoft提供的(即数据库等),那么Dispose(来自IDisposable)的处理很可能已经处理完毕,由你来调用它。例如,使用数据库的标准做法如下:

//...
using (IDataReader dataRead = new DataReaderObject())
{
   //Call database
}

这与写作基本相同:

IDataReader dataRead = null;
try
{
    dataRead = new DataReaderObject()
    //Call database
}
finally
{
    if(dataRead != null)
    {
        dataRead.Dispose();
    }
}

根据我的理解,通常很好的做法是在继承自IDisposable的对象上使用前者,因为它可以确保正确释放资源。

至于自己使用IDisposable,实现取决于您。一旦从中继承,就应该确保该方法包含处理您可能手动创建的任何数据库连接所需的代码,释放可能保留的资源或阻止对象被破坏,或者仅清理大型资源池(如图像) )。这还包括非托管资源,例如,标记在“不安全”内的代码。 block本质上是非托管代码,可以允许直接内存操作,这肯定需要清理。

术语“非托管资源”有点用词不当。基本概念是配对操作 - 执行一个操作会产生执行某些清理操作的需要。打开文件需要关闭它。拨打调制解调器需要挂断。系统可能无法执行清理操作,但后果可能很严重。

当一个对象被称为“保持非托管资源”时,真正的意思是该对象具有在某个其他实体上执行某些必需的清理操作所需的信息和动力,并且没有特别理由相信其他地方存在信息和动力。如果完全放弃了具有此类信息和推动力的唯一对象,则永远不会发生所需的清理操作。 .Dispose的目的是强制对象执行任何所需的清理,以便可以安全地放弃它。

为了在不先调用Dispose的情况下为放弃对象的代码提供一些保护,系统允许类注册“finalization”。如果放弃了已注册类的对象,系统将为该对象提供在其他实体被放弃之前执行清理操作的机会。然而,不能保证系统会多快地注意到对象已被放弃,并且各种情况可能会阻止对象被提供清理的机会。术语“管理资源”是指“管理资源”。有时候用来指代一个必须在它被放弃之前执行一些清理的对象,但如果有人无法调用Dispose,它会自动注册并尝试执行这样的清理。

为什么对你很重要?

如果可能的话,我将一次性物品的范围包裹在使用中。这会在使用结束时调用dispose。如果不是,我会在不再需要该对象时显式调用dispose。

是否需要原因1或原因2。

是的,您负责调用 Dispose 方法 - 或者更好地使用使用语句。如果一个对象正在实现 IDisposable ,那么无论如何都应该处理它。

using (var myObj = new Whatever())
{
   // ..
}

类似于

{
  var myObj;
  try
  {
     myObj = new Whatever();
     // ..
  } 
  finally
  {
    if (myObj != null)
    {
      ((IDisposable)myObj).Dispose();
    }
  }
} // object scope ends here

编辑:感谢Talljoe添加了try / finally - 哇,这很难实现:)

EDIT2:我不是说您应该使用第二种变体。我只想表明“使用”对于一堆代码而言,这是一个很好的语法糖,可能会变得非常混乱,很难做对。

这里缺少的是终结者 - 我的习惯是如果我实现IDisposable我也有一个终结器来调用Dispose()以防万一我的客户端没有。是的,它会增加开销但是如果调用Dispose()而不是GC.SuppressFinalize(this)调用则会消除它。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top