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

What are the rules for when dispose() is required?

虽然我已经编码了一段时间,但我真的只是勉强进入我所说的中级编码器。于是明白了dispose()的原理,就是释放为变量and/or资源预留的内存。我还发现有时使用 EF 我必须 dispose() 才能使其他操作正常工作。我不明白的是什么时候需要发布,什么时候使用 dispose()。

例如,我们不处理字符串、整数或布尔值等变量。但是在某个地方我们跨越 'a line' 并且我们使用的变量 and/or 资源需要被释放。我不明白这条线在哪里。

在知道何时使用 dispose() 时,是否有一个单一的原则或一些广泛的原则可以应用?

我阅读了这些 SO posts (a specific situation, more about how rather than when ),但我觉得我不了解知道何时使用 dispose() 的基础知识。我看到的一条评论询问当变量超出范围时是否释放内存,这引起了我的注意,因为直到我看到响应是否定的,它不会仅仅因为超出范围而被释放,我会认为当它超出范围时它确实会被释放。我不想成为第 2 个 link 中的一个人所说的 'clueless developer',尽管我认为这有点苛刻。我们中的一些人仍在学习。

这就是为什么我的问题是“什么决定了何时真正需要 dispose()?”

我的问题少了一个 how,多了一个 when。当然评论如何会有用,但即使调用 dispose() 的方法是 Using 语句,我仍然需要知道什么时候。

编辑原始问题: 我知道这是一个很长的解释,因为 标记为重复评论注释 请求,它不是咆哮,我只是不知道如何确保我把注意力集中在我的确切问题上。很多时候,我们只是在问问题的方式上绊倒了。正如我在这篇长文末尾提到的,假设我们到达那里,在我们专注于我的问题之后,我将编辑所有这些内容。根据我所阅读的内容,我认为这是一个重要的问题。

建议的“答案”post 很好 post,但并没有真正回答我的问题。 CodeNotFound 下面的评论也给出了 great link 但它也没有真正回答我的问题。我已经提供了关于这些 post 的评论,以尝试帮助完善我的精确问题:

When should I dispose my objects in .NET?:第一个答案以

的评论开头

Disposable objects represent objects holding a valuable resource which the CLR is not intrinsically aware of.

不幸的是,我不明白术语“一次性对象......CLR 本质上不了解”包括什么。 这就是我要问的。我如何知道某物是否属于我必须处置的类别?我们一直定义要在代码中使用的东西。我们什么时候越过这条线,它变成了我需要处理的对象()?顺便说一句,我注意到 post 的作者从未标记过答案。我不知道这是否意味着他觉得问题没有得到回答,或者只是他的跟进不力,但希望我已经提炼了一些我希望理解的内容。当您仔细查看答案时,它们并没有真正解决 哪些 对象需要开发人员的操作来处理它们的问题,或者我可能会如何知道如何识别哪些 个对象。我只是不知道我创建的哪些对象或东西需要我负责处理。我知道 GC 和其他规定开始发挥作用,但同样,这只是 如何。似乎很清楚的是,大多数有经验和专业的开发人员都知道什么时候他们创建的东西需要被处理掉。我不明白怎么知道那个.

Proper use of the IDisposable interface:显然是一个受欢迎的答案(1681 票),但标记的答案以

开头

The point of Dispose is to free unmanaged resources".

好的,但我的问题是我如何通过查看某些东西来知道它是 非托管资源?而且我不明白下面的说明如何适用于 需要处理的内容

If you found it in the .NET framework it's managed. If you went poking around MSDN yourself, it's unmanaged... and you're now responsible for cleaning it up."

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

在那之后,答案继续详细讨论 如何 处理 (),但我仍然停留在 什么 需要处理。为了让我的话题更加复杂,那位作者后来说“所以现在我们将......

get rid of unmanaged resources (because we have to), and

get rid of managed resources (because we want to be helpful)

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

For anyone who likes the style of this answer (explaining the why, so the how becomes obvious)...

我知道作者在建议其他文章,但作者关于理解“为什么”使“如何”显而易见的建议并不真正合理,因为对一个人来说显而易见的事情对另一个人来说并不总是显而易见的。即便如此,作者更关注原因和方式,而我的问题是关于何时,意思是什么 需要处理 (),而不是 我完成它时。我知道什么时候我完成了事情,我只是不知道哪些事情我负责什么时候 我受够了。

对于大多数开发人员来说 需要处理什么 可能是显而易见的或本能的(),但对我来说并不明显而且我相信很多其他人处于我的经验阶段我希望就 什么 进行更集中的对话。当然 why 是有用的,但在这种情况下只有当 why 附加到 what 时。例如:您必须处置 DbContext 因为 CLR 不会处置它 - 因为 解释 为什么 ,但在这种情况下,DbContext 是必须处理的 what

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

再次,我明白内存释放很重要,而且很多经验和专业知识都用于学习 whyhow,但我仍然难以理解 需要处理什么 。一旦我理解 什么 我必须处理 (),然后我就可以开始努力学习 如何 去做。

所以这仍然是一个糟糕的问题吗?稍后我将删除所有这些解释,让 post 更加简洁,假设我们能够更加关注我的问题。

最终编辑: 虽然我在上面说过我会编辑掉我原先认为问题中不必要的文本,但我认为最好还是保留它。我认为提问的方式有可能帮助我们理解答案。即使答案永远不会改变,但如果我们不将答案与我们在脑海中构建问题的方式联系起来,我们可能无法真正理解答案。因此,如果这个问题的构建方式与某人有关,我鼓励您完整阅读标记为答案的 post 以及评论。虽然最后的答案非常简单,但有大量的历史和背景对于理解这个问题的答案很重要。为清楚起见,在有关 dispose() 的讨论过程中,答案也被来回编辑。享受...

Dispose 模式可用于清除托管和非托管资源。如果您的 class 中有非托管资源,根据 proper IDisposable implementation 您必须同时拥有 Dispose 和 Finalize 方法。

How do I know what GC knows/doesn't?

GC knows/interested 仅关于托管对象。 GC 应该清除那些没有任何强引用的对象。它不知道你的逻辑。举一个简单明了的例子。

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

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

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

The post also complicates things saying managed resources may include unmanaged resources. Wow. This is getting deeper than I initially imagined. I'm still looking to easily recognize how to know what needs to be disposed. Is it the case this is a complicated list w/ conditions and context?

没错,托管对象也可以拥有非托管资源。所有这些托管对象都有它们的 finalize 方法来清除非托管资源。

Wonder why you need finalize method in addition to the Dispose?

非托管资源会造成最危险的内存泄漏,因为此类内存泄漏会一直占用内存,直到您重新启动 PC。所以,他们非常糟糕。

假设有托管实例 InsA 和一些非托管实例。 InsA 也以清除非托管资源的方式实现了 Dispose 方法。但是如果你不't/forget调用Dispose方法会发生什么?它永远不会清除非托管内存。这就是最终确定进入功能的原因。因此,如果您 forget/don 不调用 Dispose,终结将保证以释放非托管资源的方式执行 Dispose。

I understand the principle of dispose(), which is to release memory reserved for variables and/or resources.

理解处理的目的。 不是释放与变量关联的内存。

What I don't understand is just exactly what requires a release, when to employ dispose().

当您确定完成后,释放任何实现 IDisposable 的东西。

For example, we don't dispose variables like string, integer or booleans. But somewhere we cross 'a line' and the variables and/or resources we use need to be disposed. I don't understand where the line is.

这条线是为你划定的。当一个对象实现了 IDisposable 时,它​​应该被释放。

我注意到 变量 根本不是被处置的东西。 对象 已处理。对象不是变量,变量也不是对象。变量是 values.

的存储位置

Is there a single principle or a few broad principles to apply when knowing when to use dispose()?

一条原则:当对象是一次性的时候就丢弃。

I don't feel like I understand the basics of knowing when to use dispose().

处置所有一次性物品。

One comment I saw asked if memory is released when a variable goes out of scope, and that got my attention because until I saw the response was no, it doesn't get released just because it goes out of scope, I would have thought that it does get released when it goes out of scope.

注意你的语言使用。你混淆了作用域和生命周期,你混淆了变量和变量的内容。

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

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

还请记住,变量是存储,它可能指的是存储。当本地的生命周期结束时,与 本地 关联的存储可能会被回收。但是,无法保证与 本地引用的内容 关联的存储会在那时或以后被回收。

So that's why my question is "What determines when a dispose() is really necessary?"

当对象实现 IDisposable 时,Dispose 是必需的。 (有少量不需要处理的一次性对象。例如任务。但作为一般规则,如果它是一次性的,请处理它。)

My question is less one of how and more one of when.

只在用完后才处理。之前没有,以后也没有。

When should I dispose my objects in .NET?

在对象实现 IDisposable 时释放对象,您就可以使用它们了。

How do I know if something falls into the category of what I must dispose?

当它实现 IDisposable 时。

I just don't know what objects or things I create require that I am responsible for disposing.

一次性的

most experienced and professional developers know when something they've created needs to be disposed of. I don't understand how to know that.

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

The point of Dispose is to free unmanaged resources". OK, but my question is how do I know by looking at something that it is an unmanaged resource?

它实现了 IDisposable。

I don't understand how to use that type of explanation to categorize what I need to dispose() and what I don't. There's all kinds of stuff in the .net framework; how do I separate out things that require I dispose() of them? What do I look at to tell me I'm responsible for it?

检查它是否是 IDisposable。

After that, the answer goes on to speak at great length about how to dispose(), but I'm still stuck back at what needs to be disposed.

任何实现 IDisposable 的东西都需要被释放。

my question is about when, meaning what needs to be disposed(), as opposed to when I'm done with it. I know when I'm done with things, I just don't know which things I'm responsible for when I'm done with them.

实现 IDisposable 的东西。

I was hoping there is a general principle for what must be disposed rather than a long list of specific items which would not be particularly useful for people like me who are looking for simple guidelines.

简单的准则是您应该处理一次性物品。

Again, I get it that memory release is important, and also that a lot of experience and expertise goes into learning why and how, but I'm still left struggling to understand what needs to be disposed. Once I understand what I have to dispose(), then I can begin the struggle to learn how to do it.

通过调用 Dispose() 处理实现 IDisposable 的事物。

So is this still a bad question?

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

Your patience is a kindness.

感谢您以幽默的方式回答这个有点愚蠢的问题!

WRT scope != lifetime & variables != objects, very helpful.

这些很容易混淆,而且在大多数情况下,区别不大。但我发现,那些难以理解一个概念的人往往对含糊和不精确的东西一点也不好。

In VS is it so simple as looking in Object Browser / Intellisense to see if the object includes Dispose()?

绝大多数时候,是的。

有一些不明显的极端情况。正如我已经提到的,从 TPL 团队那里得到的智慧是处理 Task 对象不仅没有必要,而且可能适得其反。

还有一些类型实现了 IDisposable,但使用 "explicit interface implementation" 技巧使 "Dispose" 方法只能通过强制转换为 IDisposable 来访问。在大多数情况下,对象本身都有一个 Dispose 的同义词,通常称为 "Close" 或类似的东西。我不太喜欢这种模式,但有人用它。

对于那些对象,using 块仍然有效。如果出于某种原因您想在不使用 using 的情况下显式处理此类对象,则 (1) 调用 "Close" 方法或调用它的任何方法,或者 (2) 强制转换为 IDisposable 并处理它。

普遍的看法是:如果对象是一次性的,那么处理它并没有什么坏处,当你用完它时处理它是一个很好的做法。

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

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

WRT implementing IDisposable @Brian's comment suggests in "normal" coding I likely don't need to. So would I only do that if my class pulled in something unmanaged?

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

(1) 常见场景:你正在编写一个长期持有另一个 IDisposable 对象的对象,并且 "inner" 对象的生命周期与 [=185 的生命周期相同=]对象。

例如:您正在实现一个记录器 class,它打开一个日志文件并保持打开状态直到日志关闭。现在你有一个 class 拿着一次性用品,所以它本身也应该是一次性用品。

我注意到在这种情况下 "outer" 对象不需要 finalizable。只是一次性的。如果出于某种原因从未在外部对象上调用处置,则内部对象的终结器将负责完成。

(2) 罕见情况:您正在实现一个新的 class,它向操作系统或其他外部实体请求必须积极清理的资源,并且该资源的生命周期是相同的作为持有它的对象的生命周期。

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

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

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

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

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

一个你没有问但我还是会回答的问题:

Are there scenarios in which I should not be implementing IDisposable?

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

  • 改变世界
  • 在新世界做事
  • 还原更改

因此,例如 "impersonate an administrator, do some admin tasks, revert to normal user"。或 "start handling an event, do stuff when the event happens, stop handling the event"。或 "create an in-memory error tracker, do some stuff that might make errors, stop tracking errors"。等等。你得到了一般模式。

这不适合一次性模式,但这并不能阻止人们编写 classes 表示任何非托管资源,但仍然像他们那样实现 IDisposable。

这种观点使我成为少数;许多人对这种机制的滥用没有任何问题。但是当我看到一个disposable的时候我觉得"the author of this class wishes me to be polite and clean up after myself when I am good and ready."但是实际的契约class往往是"you must dispose this at a particular point in the program and if you do not then the rest of the program logic will be wrong until you do"。当我看到一次性用品时,这不是我希望必须执行的合同。我希望我必须做出善意的努力,在我方便的时候清理资源。

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

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

GC 从不 为您调用 .Dispose()。您必须始终这样做 - 直接或通过终结器。

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

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

让我们分解一下:

managed:笼统地说,这意味着资源完全在 .NET/CLR 内。例如,非 .NET C++ 库分配的内存 不是 由 GC 释放。

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

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

在达到内存限制之前:GC 监控"memory pressure" 并确保在需要时释放内存。它使用一系列算法来决定何时最有效地执行此操作,但重要的是要注意,GC 会自行决定何时释放资源(对您的程序而言是不确定的)。


当依赖 GC 不合适时,这给我们留下了几种情况:

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

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

任何实现 IDisposable 实例化的对象都必须通过调用 Dispose 或使用 using 块(负责为您调用 Dispose )来清理.