什么时候需要dispose()的规则是什么?

虽然我已经编写了一段时间,但我真的只是进入了我所谓的中级编码器。 所以我理解dispose()的原理,即释放为变量和/或资源保留的内存。 我也发现有时使用EF我必须dispose()以便其他操作正常工作。 我不明白的只是需要发布的内容,何时使用dispose()。

例如,我们不处理像字符串,整数或布尔值这样的变量。 但是在某个地方我们越过“一条线”,我们使用的变量和/或资源需要被处理掉。 我不明白这条线在哪里。

在知道何时使用dispose()时,是否有一个原则或几个广泛的原则适用?

我读了这些SOpost( 具体情况 , 更多关于如何而不是何时 )但我不觉得我理解何时使用dispose()的基础知识。 我看到一条评论询问当变量超出范围时是否释放内存,这引起了我的注意,因为直到我看到响应为否,它才会因为超出范围而被释放,我会想到当它超出范围时它会被释放。 我不想成为第二个链接中的一个人称为“无能的开发者”,虽然我认为这有点苛刻。 我们中的一些人还在学习。

所以这就是为什么我的问题是“什么时候确定需要处置?”?

我的问题不再那么多, 何时 。 当然评论如何有用,但即使调用dispose()的方法是一个Using语句,我仍然需要知道何时。

编辑原始问题:我知道这是一个很长的解释, 标记为重复的评论笔记请求,这不是一个咆哮,我只是不知道如何确保我把焦点放在我的精确问题上。 很多时候,我们只是匆匆走过我们问的方式。 正如我在这篇长篇文章的最后提到的那样,在我们专注于我的问题之后,我将编辑所有这些,假设我们到达那里。 根据我所读到的内容,我认为这是一个重要的问题。

提议的“答案”post是一篇很棒的post,但并没有真正回答我的问题。 CodeNotFound下面的评论也提供了一个很好的链接,但它也没有真正回答我的问题。 我提供了有关这些post的评论,试图帮助完善我的确切问题:

我什么时候应该在.NET中处理我的对象? :第一个答案以评论开头

一次性对象表示持有CLR本质上不知道的有价值资源的对象。

不幸的是,我不明白“ 一次性对象…… CLR本质上没有意识到 ”这一术语包括在内。 就是我的要求。 我怎么知道某件事是否属于我必须处理的范畴? 我们一直在代码中定义要使用的东西。 我们什么时候越线,它成为我需要处置的对象()? 顺便说一句,我注意到那篇文章的作者从未标出答案。 我不知道这是否意味着他不觉得这个问题得到了回答,或者他的跟进是否很差,但我希望我能够提炼一点我希望了解的内容。 当你仔细研究答案时,他们并没有真正解决哪些对象需要开发人员处理它们的问题,或者我如何知道如何识别哪些对象。 我只是不知道我创建的对象或事物需要我负责处理。 而且我认为GC和其他条款发挥作用,但同样,这就是如何 。 显而易见的是,大多数经验丰富的专业开发人员都知道他们创建的东西何时需要被处理掉。 我不明白怎么知道。

正确使用IDisposable界面 :显然是一个流行的答案(1681 upvotes),但明确的答案开始于

Dispose的目的是释放非托管资源“。

好的,但我的问题是如何通过查看它是非托管资源的东西来了解? 我不明白下面的注释如何适用于需要处理的内容。

如果你在.NET框架中找到它,那么它就是托管的 。 如果你自己去探索MSDN,它是不受管理的 …你现在负责清理它。“

我不明白如何使用这种类型的解释来分类我需要处理的东西()和我不需要的东西。 .net框架中有各种各样的东西; 如何分离出需要我处理它们的东西? 我该怎么看我告诉我我对此负责?

在那之后,答案继续谈论如何处置(),但我仍然停留在需要处理的东西上。 为了让我对这个话题进一步复杂化,该作者后来说:“现在我们将……

摆脱非托管资源(因为我们必须),

摆脱托管资源 (因为我们想要帮助)

所以现在我需要考虑处理一组使用内存的全新对象,我不知道它们是什么。 该答案的作者后来说

对于喜欢这种答案风格的人(解释原因,如何变得明显)……

我理解作者是在暗示其他文章,但作者的建议是理解“为什么”使得“如何”显而易见并不合法,因为对一个人来说显而易见的事情对另一个人来说并不总是显而易见的。 甚至在那时,作者更多地关注为什么以及如何 ,我的问题是什么时候 ,意味着什么需要被处理(),而不是我完成它。 我知道我做完事情的时候,我只是不知道我完成它们我要负责哪些事情。

大多数开发人员可能明显或本能地需要处理(),但这对我来说并不明显,我确信很多其他人在我的经验阶段,我希望得到更有针对性的对话。 当然为什么有用,但在这种情况下,只有当为什么附加到什么时 。 例如:你必须处理一个DbContext, 因为 CLR不会处理它 – 因为解释了原因 ,但在这种情况下,它是必须处理的DbContext。

我希望有一个必须处理的一般原则,而不是一长串的具体项目,这对于像我这样寻求简单指导的人来说并不是特别有用。

再一次,我认为记忆释放很重要,而且很多经验和专业知识都用于学习为什么如何 ,但我仍然在努力去理解需要处理什么 。 一旦我理解我必须处理的东西(),那么我就可以开始学习如何去做了。

这还是一个不好的问题吗? 我将稍后编辑所有这些解释,以使我们能够更加专注于我所要求的内容,使post更加简洁。

最终编辑:虽然我在上面说过,但我会在问题中编辑出我原本认为不必要的文字,我认为最好留下它。我认为提出问题的方式有可能帮助我们理解答案。 即使答案永远不会改变,如果我们没有将答案与我们在脑海中解决问题的方式联系起来,我们可能也不会真正理解答案。 因此,如果这个问题构成的方式与某人有关,我建议您完全阅读标记为答案的post以及评论。 虽然最后的答案非常简单,但是有很多历史和背景对于理解这个问题的答案很重要。 为了清楚起见,答案也在有关dispose()的讨论的生命周期中来回编辑。 请享用…

我理解dispose()的原理,即释放为变量和/或资源保留的内存。

明白处置的目的。 它不是用于释放与变量相关的内存。

我不明白的只是需要发布的内容,何时使用dispose()。

当您确定已完成IDisposable时,请处置任何实现IDisposable的内容。

例如,我们不处理像字符串,整数或布尔值这样的变量。 但是在某个地方我们越过“一条线”,我们使用的变量和/或资源需要被处理掉。 我不明白这条线在哪里。

这条线为你划定了界限。 当一个对象实现IDisposable时,它应该被处理掉。

我注意到变量不是完全处理的东西。 对象被丢弃。 对象不是变量,变量不是对象。 变量是值的存储位置。

在知道何时使用dispose()时,是否有一个原则或几个广泛的原则适用?

一个原则:当物体是一次性的时候处理。

我不觉得我理解何时使用dispose()的基础知识。

处理所有可丢弃的物品。

我看到一条评论询问当变量超出范围时是否释放内存,这引起了我的注意,因为直到我看到响应为否,它才会因为超出范围而被释放,我会想到当它超出范围时它会被释放。

在使用语言时要小心。 您将范围和生命周期混淆,并且您将变量与变量的内容混淆。

第一:变量的范围是程序文本的区域,其中该变量可以通过名称引用 。 变量的生命周期是程序执行期间的时间段,其中变量被认为是垃圾收集器的根。 范围纯粹是一个编译时的概念,生命周期纯粹是一个运行时的概念。

范围和生命周期之间的联系是局部变量的生命周期通常在控制进入变量范围时开始,并在它离开时结束。 但是,各种事物可以改变本地的生命周期,包括在迭代器块中或在异步方法中被关闭。 抖动优化器还可以缩短或延长本地的寿命。

还要记住,变量是存储,它可能指的是存储。 当本地的生命周期结束时,可以回收与本地相关联的存储。 但无法保证与当地所指的东西相关的存储将在当时或之前被回收。

所以这就是为什么我的问题是“什么时候确定需要处置?”?

当对象实现IDisposable时,必须进行Dispose。 (有少量的一次性物品不需要处理。例如,任务。但作为一般规则,如果是一次性的,请将其丢弃。)

我的问题不再那么多,何时。

完成后只处理一件事。 不是之前,也不是之后。

我什么时候应该在.NET中处理我的对象?

在实现IDisposable时处理对象,并且您已完成使用它们。

我怎么知道某件事是否属于我必须处理的范畴?

当它实现IDisposable时。

我只是不知道我创建的对象或事物需要我负责处理。

一次性的。

最有经验和专业的开发人员知道他们创建的东西何时需要被处理掉。 我不明白怎么知道。

他们检查物体是否是一次性的。 如果是,他们会处置它。

Dispose的目的是释放非托管资源“。好的,但我的问题是如何通过查看它是非托管资源的东西来了解?

它实现了IDisposable。

我不明白如何使用这种类型的解释来分类我需要处理的东西()和我不需要的东西。 .net框架中有各种各样的东西; 如何分离出需要我处理它们的东西? 我该怎么看我告诉我我对此负责?

检查它是否是IDisposable。

在那之后,答案继续谈论如何处置(),但我仍然停留在需要处理的东西上。

任何实现IDisposable的东西都需要处理。

我的问题是什么时候,意味着什么需要处理(),而不是当我完成它。 我知道当我做完事情的时候,我只是不知道当我完成它们时我要负责哪些事情。

实现IDisposable的东西。

我希望有一个必须处理的一般原则,而不是一长串的具体项目,这对于像我这样寻求简单指导的人来说并不是特别有用。

简单的指导方针是你应该丢弃一次性用品。

再一次,我认为记忆释放很重要,而且很多经验和专业知识都用于学习为什么和如何,但我仍然在努力去理解需要处理什么。 一旦我理解了我必须处理的东西(),那么我就可以开始学习如何去做了。

通过调用Dispose()来处理实现IDisposable的东西。

这还是一个不好的问题吗?

这是一个非常重复的问题。

你的耐心是一种善意。

感谢你在幽默中采取这个有点愚蠢的答案!

WRT范围!=生命周期和变量!=对象,非常有帮助。

这些都是非常混乱的,而且大多数时候,它几乎没有什么区别。 但我发现,那些正在努力理解一个概念的人往往并不是模糊和不精确。

在VS中它是如此简单,如在Object Browser / Intellisense中查看该对象是否包含Dispose()?

绝大多数时候,是的。

有一些不起眼的角落案件。 正如我已经提到的,TPL团队所获得的智慧是,处理Task对象不仅是不必要的,而且可能适得其反。

还有一些类型实现了IDisposable,但是使用“显式接口实现”技巧使“Dispose”方法只能通过强制转换为IDisposable来访问。 在大多数情况下,对象本身存在Dispose的同义词,通常称为“关闭”或某些此类事物。 我不太喜欢这种模式,但有些人使用它。

对于这些对象, using块仍然有效。 如果由于某种原因你想要显式地处理这些对象而不使用则那么(1)调用“Close”方法,或者调用它,或者(2)转换为IDisposable并处理它。

一般的智慧是:如果物体是一次性的,那么处理它并不会有害,当你完成它时这是一个很好的做法。

原因是:一次性物品通常代表稀缺的共享资源。 例如,某个文件可能会以某种模式打开,该模式在您打开文件时拒绝其他进程访问该文件的权限。 这是礼貌的事情,以确保文件一旦完成就关闭。 如果一个进程想要使用一个文件,那么很快就会有另一个进程。

或者一次性可能代表图形对象。 如果一个进程中有超过一万个活动对象,操作系统将停止发出新的图形对象,所以当你完成它们时你必须让它们离开。

WRT实施IDisposable @ Brian的评论表明我可能不需要“正常”编码。 如果我的class级不受管理,我会这样做吗?

好问题。 您应该在两种情况下实现IDisposable。

(1)常见场景:您正在编写一个长时间保留在另一个IDisposable对象上的对象,并且“内部”对象的生命周期与“外部”对象的生命周期相同。

例如:您正在实现一个记录器类,该类打开日志文件并在日志关闭之前保持打开状态。 现在你有一个固定一次性的类,所以它本身也应该是一次性的。

我注意到在这种情况下不需要可以最终确定 “外部”对象。 只是一次性的 如果由于某种原因从未在外部对象上调用dispose,则内部对象的终结器将负责完成。

(2)罕见的情况:您正在实现一个新类,该类向操作系统或其他外部实体询问必须积极清理的资源,并且该资源的生命周期与保存在其上的对象的生命周期相同。

在这种非常罕见的情况下,您首先应该问自己是否有任何方法可以避免它。 对于初学者到中级程序员来说,这是一个糟糕的情况。 您真的需要了解CLR如何与非托管代码交互以使这些东西变得扎实。

如果你无法避免它,你应该不想尝试自己实现处理和终结逻辑, 特别是如果非托管对象由Windows句柄表示。 应该已经有大多数由句柄表示的OS服务的包装器,但如果没有,你想要做的是仔细研究IntPtr,SafeHandle和HandleRef之间的关系。 IntPtr,SafeHandle和HandleRef – 解释

如果您确实需要为非托管,非基于句柄的资源编写处理逻辑,并且资源需要通过最终确定来支持处理,那么您将面临重大的工程挑战。

标准的配置模式代码可能看起来很简单,但是在编写正确的终结逻辑时存在真正的微妙之处,这些逻辑在错误条件下是健壮的。 记住,终结器在不同的线程上运行,并且可以在线程中止场景中与构造函数同时在该线程上运行 。 编写线程安全逻辑, 一个对象仍然在另一个线程上构建时清理可能非常困难,我建议不要尝试。

有关编写终结器的挑战的更多信息,请参阅我关于该主题的系列文章: http : //ericlippert.com/2015/05/18/when-everything-you-know-is-wrong-part-one/

你没问的问题,但我会回答:

是否存在我应该实现IDisposable的情况?

是。 许多人在希望拥有具有以下语义的编码模式时实现IDisposable:

  • 改变世界
  • 在新世界做点什么
  • 还原更改

因此,例如,“冒充管理员,执行一些管理任务,恢复普通用户”。 或者“开始处理事件,在事件发生时执行操作,停止处理事件”。 或者“创建一个内存中的错误跟踪器,做一些可能会出错的东西,停止跟踪错误”。 等等。 你得到了一般模式。

这不适合一次性模式,但这并不能阻止人们编写代表任何非托管资源的类,但仍然可以实现IDisposable。

这种观点使我成为少数; 滥用这种机制,很多人都没有任何问题。 但是,当我看到一个一次性的时候,我觉得“这个class级的作者希望我在我好的和准备好之后能够礼貌地清理自己。” 但是这个类的实际合同通常是“你必须在程序中的某个特定位置处理它,如果你不这样做,那么程序逻辑的其余部分将是错误的,直到你做”。 这不是我期望在看到一次性用品时必须实施的合同。 我希望在方便的时候,我必须努力清理资源。

如果您有一个实现IDisposable的对象,那么您必须始终在该对象上显式调用.Dispose() 。 如果它没有实现IDisposable那么很明显你不会调用.Dispose()因为你不能。

如果您正在编写自己的对象,那么关于是否实现IDisposable的规则就是:如果您的对象持有对非托管对象的引用,或者如果它包含对实现IDisposable对象的引用,那么它必须实现IDisposable

GC 从不为您调用.Dispose() 。 你必须经常这样做 – 直接或通过终结者。

GC 可能 (很可能,但并非总是)调用终结器,因此您可以编写终结器来调用dispose,但要小心您正确实现了一次性模式,并确保您了解终结器可能不会运行如果你的dispose方法做了非常重要的事情,最好在你失去对象的引用之前直接调用.Dispose()

垃圾收集器(GC)保证在达到内存限制之前释放不再使用的 内存资源。

让我们打破这个:

托管 :松散地,这意味着资源完全在.NET / CLR中。 例如,非.NET C ++库分配的内存不会由GC发布。

内存 :GC仅提供内存使用保证。 存在许多其他资源类型,例如文件句柄。 GC没有逻辑来确保方便地释放文件句柄。

不再使用 :这意味着所有引用该内存的变量都已终止其生命周期。 正如Eric Lippert解释的那样,终生!=范围。

达到内存限制之前 :GC监视“内存压力”并确保在需要时释放内存。 它使用一堆算法来决定何时最有效地执行此操作,但重要的是要注意GC决定何时自行释放资源(不确定地发布到您的程序)。


当依赖GC不合适时,这给我们留下了几个场景:

  • 该对象引用非托管资源(包括引用引用非托管资源的托管对象)。
  • 该对象引用必须释放的内存以外的资源。
  • 该对象引用必须在代码中的特定点释放的资源(确定性地)。

在任何这些情况下,对象都应该实现IDisposable来处理资源清理。

必须通过调用Dispose或使用using块(它负责为您调用Dispose )来清除实例化IDisposable任何实例化对象。

Dispose模式可用于清除托管和非托管资源。 如果您的类中有非托管资源,则根据正确的IDisposable实现,您必须同时具有Dispose和Finalize方法。

我怎么知道GC知道/不知道什么?

GC只知道/感兴趣的托管对象。 GC应该清除那些没有任何强引用的对象。 它不知道你的逻辑。 一个简单而明显的例子。

假设您有一个持续很长时间的MainView实例,并且您创建另一个订阅MainView实例中的事件的LittleView。

然后关闭LittleView,它就会消失。 你知道你不再需要那个LittleView实例了。 但是GC不知道你是否还需要LittleView,因为MainWindow有一个活动的事件订阅。

因此,GC不会费心从内存中清除LittleView实例。 因此,您应该做的是在关闭视图时取消订阅该事件。 然后GC知道没有对LittleView的强引用,并且它是可以访问的。

该post还使管理资源可能包含非托管资源的事情变得复杂。 哇。 这比我最初设想的要深刻得多。 我仍然希望能够轻松地认识到如何知道需要处理什么。 这是一个带条件和背景的复杂列表吗?

这是正确的,托管对象也可以拥有非托管资源。 所有这些托管对象都有其finalize方法来清除非托管资源。

想知道除了Dispose之外还需要finalize方法吗?

非托管资源可能会造成最危险的内存泄漏,因为在重新启动PC之前,内存泄漏可能会占用内存。 所以,他们非常糟糕。

假设存在托管实例InsA,其中一些是非托管的。 InsA以一种清除非托管资源的方式实现了Dispose方法。 但是如果你没有/忘记调用Dispose方法会发生什么? 它永远不会清除非托管内存。 这就是Finalization进入function的原因。 因此,如果您忘记/不调用Dispose,则最终确定将以释放非托管资源的方式执行Dispose。