我了解dispose()的原则,它是用于释放变量和/或资源所保留的内存。
您不理解dispose的目的。它并不是为了释放与变量相关联的内存。
当您确定完成使用时,请处理实现IDisposable接口的任何内容。
例如,我们不会处理像字符串、整数或布尔值之类的变量。但是,在某个地方,我们越过了“一个界线”,我们使用的变量和/或资源需要被处理。我不知道这个界线在哪里。
该界限已经划定好了。当对象实现IDisposable接口时,应进行处理。
请注意,变量根本没有被处理。对象被处理。对象不是变量,变量也不是对象。变量是存储值的存储位置。
有单一原则可遵循:当对象是可处理的时请进行处理。
我感觉自己不理解何时使用dispose()的基础知识。
请处理所有可处理的对象。
一个注释问到了当变量超出作用域时是否释放内存,这引起了我的注意,因为在看到回答是不会,变量仅仅因为超出范围而被释放的时候,我曾认为变量会被释放。
请注意语言使用的准确性。您混淆了作用域和生命周期,并将变量与变量的内容混为一谈。
首先:变量的作用域是“程序文本中一个变量可以被名称引用的区域”。变量的生命周期是在程序执行期间变量被视为垃圾收集器的根的时间段。作用域是纯粹的编译时概念,生命周期是纯粹的运行时概念。
作用域和生命周期之间的联系是局部变量的生命周期通常从控制进入变量的范围开始,到离开变量的范围结束。然而,各种事情都可以改变局部变量的生命周期,包括闭包、迭代器块或异步方法中的变量。Jitter优化器还可能缩短或延长局部变量的寿命。请您记住,变量是存储器,并且它可以引用存储器。当局部变量的生命周期结束时,与“局部变量”相关联的存储器可能会被收回。但是,不能保证与“局部变量引用的东西”相关联的存储器在此时或任何时候都会被回收。
因此,我的问题是“什么确定了dispose()何时真正需要?”
如果对象实现了IDisposable,则需要使用Dispose。(有少量可处理但不必处理的对象。例如,任务。但通常情况下,如果它是可处理的,请处理它。)
我的问题不是如何而是何时处理。
只有在完成使用后才处理事物。不早于此时,也不晚于此时。
在.NET中应何时处理对象?
当它们实现IDisposable并且你已经使用它们时,请处理对象。
如何知道某个东西属于我必须处理的范畴?
当它实现IDisposable时。
我不知道自己创建的对象或事物是否需要我负责处理。
需要处理的对象。
大多数经验丰富和专业的开发人员知道他们创建的内容什么时候需要处理。我不知道如何知道这一点。
他们检查对象是否可处理。如果可以,他们就会处理它。
Dispose的重点是释放非托管资源。好的,但我的问题是通过查看某些内容如何知道它是非托管资源?
它实现IDisposable。
我不明白如何使用这种类型的解释来分类需要我dispose()和不需要我dispose()的内容。在.NET框架中有各种各样的东西;我该如何区分需要我负责处理的内容?我应该查看哪些内容来告诉我我要负责它?
请检查是否实现了IDisposable。
之后,答案详细说明了如何dispose(),但我还停留在需要被处理的内容方面。
任何实现了IDisposable的事物都需要被处理。
我的问题是关于“何时”,意思是与“dispose()”相比,“我需要处理什么”,而不是我什么时候完成处理。我知道何时完成处理,只是不知道哪些内容在完成处理后我需要负责处理。
那些实现了IDisposable的东西。
我希望有一个通用原则来说明必须处理哪些内容,而不是一长串具体项的清单,这对像我这样寻求简单指南的人并没有多大用处。
简单的指南是,您应该处理可以处理的物品。
再次强调,释放内存很重要,经验和专业知识也很重要,但我仍然无法理解需要处理哪些内容。一旦我了解了必须处理哪些内容,我就可以开始努力学习如何处理。
通过调用Dispose()处理实现IDisposable接口的内容。
这个问题非常重复。
感谢您以善意的方式接受这个有些愚蠢的答案!
关于作用域和生存期不相等以及变量与对象不同的问题,非常有帮助。这些经常会被混淆,但大多数情况下,这几乎没有区别。但我发现经常挣扎于理解某个概念的人并没有从模糊和不精确中受益。
在Visual Studio中,只需查看对象浏览器/智能感知,以查看对象是否包括Dispose(),绝大多数情况都是这样。但有一些偏远的情况。如我之前所述,TPL团队的共识是,处理任务对象不仅是没有必要的,而且可能会适得其反。
还有一些类型实现了IDisposable,但使用“显式接口实现”技巧使“Dispose”方法仅可以通过强制转换为IDisposable才能访问。在这种情况下,大多数情况下对象本身都有一个同义词用于释放资源,通常称为“Close”或类似的名称。我不太喜欢这种模式,但有些人使用它。
对于这些对象,using块仍将起作用。如果出于某种原因您想明确释放此类对象而不使用using,则可以(1)调用“Close”方法或其他名称,或者(2)将其强制转换为IDisposable并将其处理。
普遍看法是:如果对象是可处理的,则处理它不会有任何损害,并且在您完成后进行处理是一种良好的做法。
原因是:可处理的对象通常代表稀缺的共享资源。例如,文件可能以拒绝其他进程访问该文件的模式打开。确保在完成后尽快关闭文件是一种礼貌的做法。如果一个进程想要使用一个文件,很可能另一个进程很快就会想要使用该文件。
“一次性使用”可能代表图形对象之类的东西。如果在一个进程中活跃的图形对象超过了一万个,操作系统将停止提供新的图形对象,因此在完成任务后,必须释放它们。
关于实现IDisposable,@Brian的评论表明在“正常”的编码情况下,我可能不需要这样做。那么,只有当我的类引入了某些非托管的东西时,我才需要实现IDisposable吗?
好问题。有两种情况需要实现IDisposable。
(1) 常见情况:您正在编写一个长时间持有另一个IDisposable对象的对象,并且“内部”对象的生命周期与“外部”对象的生命周期相同。
例如:您正在实现一个日志记录器类,它打开一个日志文件并保持其打开状态,直到关闭该日志。现在,您有一个持有可释放资源的类,因此它本身也应该是可释放的。
请注意,在这种情况下,“外部”对象无需是可终结的。只需是可释放的。如果由于某种原因没有调用外部对象的dispose,则内部对象的终结器会负责终结。
(2) 罕见情况:您正在实现一个新类,该类要求操作系统或其他外部实体提供必须进行积极清理的资源,并且持有该资源的对象的生命周期与该资源的生命周期相同。
在这种极为罕见的情况下,您应首先问自己是否有任何避免它的方法。对于初学者到中级程序员来说,这是一个不好处理的情况。您真的需要了解CLR如何与非托管代码交互,以便使此问题变得牢固可靠。
如果无法避免,则应尽量不尝试自己实现处理和终结逻辑,特别是如果非托管对象由Windows句柄表示。大多数由句柄表示的操作系统服务应该已经有包装器,但如果没有,则需要仔细研究IntPtr、SafeHandle和HandleRef之间的关系。IntPtr,SafeHandle和HandleRef - 解释
如果您确实需要编写非托管的、非句柄型资源的处理逻辑,并且该资源要求在终结时进行后备,则您将面临重大的工程挑战。
标准的处理模式代码可能看起来很简单,但编写正确的终结逻辑是真正微妙的,而且要在错误情况下保持强大。请记住,终结器在单独的线程上运行,并且在构造函数和线程中止方案的情况下可能与该线程同时运行。编写安全的逻辑可以在在另一个线程上构建对象时清理它,这可能非常困难,我建议不要尝试。
关于编写finalizer的挑战,可以参考我关于这个主题的文章系列:http://ericlippert.com/2015/05/18/when-everything-you-know-is-wrong-part-one/
有一个问题你没有问但我还是会回答:
有没有一些情景下我不应该实现IDisposable接口?
是的。许多人在想要编写以下语义模式时都实现了IDisposable接口:
例如:"扮演管理员,执行一些管理任务,恢复为普通用户"。或者 "开始处理事件,在事件发生时执行一些操作,停止处理事件"。或者 "创建一个内存中的错误跟踪器,在可能产生错误的操作后,停止跟踪错误"。等等。你能理解这种模式。
这与可释放模式不符,但这并不能阻止人们编写代表没有未托管资源的类,但仍然像它们有一样实现IDisposable接口。
这个观点使我处于少数派。很多人完全没有问题这种滥用机制。但是当我看到一个可释放对象时,我认为"这个类的作者希望我在我准备好的时候礼貌地清理自己留下的东西。"但实际上这个类的合同通常是"你必须在程序的特定点处处理此对象,如果你不这样做,则剩余的程序逻辑将是错误的,直到你这样做。" 这不是我在看到可释放对象时期望要实现的合同。我期望我可以在方便的时候尽力清理资源。
IDisposable
,则这是必要的(但您可能不需要这样做)。如果您计划使用实现IDisposable
的对象,则Eric Lippert的答案就足够了。 - Brian