虽然我已经编写了一段时间,但我真的只是进入了我所谓的中级编码器。所以我理解dispose()的原理,即释放为变量和/或资源保留的内存。我也发现有时使用EF我必须dispose()才能使其他操作正常工作。我不理解的只是需要发布的内容,何时使用dispose()。
例如,我们不会处理像字符串,整数或布尔值这样的变量。但是在某个地方我们越过了一条线路'我们使用的变量和/或资源需要处理。我不明白这条线的位置。
在知道何时使用dispose()时,是否有一个原则或几个广泛的原则适用?
我阅读了这些SO帖子(a specific situation,more about how rather than when),但我不觉得我理解何时使用dispose()的基础知识。我看到一条评论询问当变量超出范围时是否释放内存,这引起了我的注意,因为直到我看到响应为否,它才会因为超出范围而被释放,我会当它超出范围时,它会被释放。我不想成为第二个链接中的一个人称为“无能的开发者”,尽管我认为这有点苛刻。我们中的一些人还在学习。
这就是为什么我的问题是"什么决定何时真正需要一个dispose()?"
我的问题不是如何,而是 时的问题。当然评论如何有用,但即使调用dispose()的方法是一个Using语句,我仍然需要知道何时。
编辑原始问题:我知道这是一个很长的解释,因为标记为重复评论备注请求,并且它不是咆哮,我只是我不知道如何确保我专注于我的精确问题。很多时候,我们只是匆匆走过我们问的方式。正如我在这篇长篇文章的最后提到的那样,在我们专注于我的问题之后,我会编辑所有这些,假设我们到达那里。根据我所读到的内容,我认为这是一个重要问题。
建议"答案" post是一篇精彩帖子,但并没有真正回答我的问题。下面的CodeNotFound评论也提供了一个伟大的链接,但它也没有真正回答我的问题。我提供了有关这些帖子的评论,试图帮助完善我的确切问题:
When should I dispose my objects in .NET?:第一个答案以
的评论开头一次性对象表示持有CLR本身不知道的有价值资源的对象。
不幸的是,我不明白术语" 一次性物品...... CLR本质上不知道"包括。 那是我在问什么。我怎么知道某件事是否属于我必须处理的范畴?我们一直在代码中定义要使用的东西。我们何时越过这条线并成为我需要处置的对象()?顺便说一句,我注意到那篇文章的作者从未标出答案。我不知道这是否意味着他没有感觉到这个问题得到了回答,或者他的跟进是否很差,但希望我能够提炼一点我希望了解的内容。当你仔细查看答案时,他们并没有真正解决哪些对象需要开发人员处理它们的行为的问题,或者我如何去了解如何识别哪个对象。我只是不知道我创建的对象或事物需要我负责处置。我认为GC和其他条款发挥作用,但同样,那只是如何。显而易见的是,大多数经验丰富的专业开发人员都知道他们创造的东西何时需要被处理掉。我不明白如何知道 。
Proper use of the IDisposable interface:显然是一个受欢迎的答案(1681赞成),但明确的答案始于
Dispose的目的是释放非托管资源"。
好的,但我的问题是,通过查看某些非托管资源,我怎么知道?我不明白后面的注释如何适用于 需要处理的内容。
如果您在.NET框架中找到它托管。如果你自己去探索MSDN,它的非托管 ......你现在负责清理它。"
我不明白如何使用这种类型的解释来分类我需要处理的东西()和我不需要的东西。 .net框架中有各种各样的东西;如何分离出需要我处理它们的东西?我该怎么看才能告诉我,我对此负责?
在那之后,答案继续讲述如何处置(),但我仍然坚持 需要做什么处置。为了让这个话题更加复杂,那位作者后来说了#34;所以现在我们将...
摆脱非托管资源(因为我们必须),和
摆脱托管资源(因为我们希望提供帮助)
所以现在我需要考虑处理一组使用内存的新对象,而我也不知道它们是什么。该答案的作者后来说
对于任何喜欢这种答案风格的人(解释原因,如何变得明显)......
我理解作者正在推荐其他文章,但作者的建议是理解"为什么"制作"如何"显而易见并不合法,因为对一个人来说显而易见的事情对另一个人来说并不总是显而易见的。即便如此,作者更多地关注为什么以及如何,我的问题是何时,意味着需要处理什么( ),而不是 时我完成了它。我知道什么时候我完成了事情,我只是不知道哪些我负责 的事情>我完成了他们。
大多数开发人员可能显而易见或本能需要处理的(),但这对我来说并不明显,而且我确信在我的阶段有很多其他人经验,我希望能够在什么上进行更有针对性的对话。当然为什么是有用的,但在这种情况下,只有当为什么附加到什么时。例如:你必须处理一个DbContext ,因为 CLR不会处理它 - 因为解释为什么,但在这种情况下,它是DbContext,它必须是什么。
我希望 必须处理的一般原则,而不是一长串的特定项目,这对于像我这样寻求简单指导的人来说并不是特别有用。
再一次,我认为记忆释放很重要,而且很多经验和专业知识都用于学习为什么和如何,但我还是仍然难以理解需要处理什么。一旦我理解什么我必须处理(),那么我就可以开始努力学习如何来做这件事了。
这还是一个不好的问题吗?我将在稍后编辑所有这些解释,以使我们能够更加专注于我所要求的内容,使帖子更加简洁。
最终编辑:虽然我上面说过,但我会在问题中编辑出我原本认为不必要的文字,我认为最好留下它。我认为问题的方式有问题了有助于我们理解答案的潜力。即使答案永远不会改变,如果我们没有将答案与我们在脑海中解决问题的方式联系起来,我们可能也不会真正理解答案。因此,如果这个问题的框架方式与某人有关,我建议您完全阅读标记为答案的帖子以及评论。虽然答案最终非常简单,但是有很多历史和背景对于理解这个问题的答案很重要。为了清楚起见,答案也在有关dispose()的讨论的生命周期中来回编辑。享受...
答案 0 :(得分:20)
我理解dispose()的原理,即释放为变量和/或资源保留的内存。
你不了解处置的目的。释放与变量关联的内存不。
我不明白的只是需要发布的内容,何时使用dispose()。
当你确定完成它时,处理任何实现IDisposable的东西。
例如,我们不会处理像字符串,整数或布尔值这样的变量。但是在某个地方我们越过了一条线路'我们使用的变量和/或资源需要处理。我不明白这条线的位置。
这条线为你划定了界限。当一个对象实现IDisposable时,它应该被丢弃。
我注意到变量不是根本不存在的东西。处置对象。对象不是变量,变量不是对象。变量是值的存储位置。
在知道何时使用dispose()时,是否有一个原则或几个广泛的原则适用?
一个原则:当物品是一次性的时候处理。
我不觉得我理解何时使用dispose()的基础知识。
处理所有可丢弃的物品。
我看到一条评论询问当变量超出范围时是否释放内存,这引起了我的注意,因为直到我看到响应为否,它才会因为超出范围而被释放我原以为它会在超出范围时被释放。
小心使用语言。您将范围和生命周期混淆,并且您将变量与变量的内容混淆。
首先:变量的范围是程序文本的区域,其中该变量可以通过名称引用。变量的生存期是程序执行期间的一段时间,其中变量被认为是垃圾收集器的根。范围纯粹是一个编译时概念,生命周期纯粹一个运行时概念。
范围和生命周期之间的联系是局部变量的生命周期通常在控制进入变量范围时开始,并在它离开时结束。但是,各种各样的东西可以改变本地的生命周期,包括在迭代器块中或在异步方法中被关闭。抖动优化器还可能缩短或延长本地的使用寿命。
还要记住变量是存储,它可能指的是存储。当本地的生命周期结束时,可能会回收与本地关联的存储。但无法保证与当地所指的东西相关的存储将在当时或之后被回收。
这就是为什么我的问题是"什么决定何时真正需要一个dispose()?"
当对象实现IDisposable时,必须进行Dispose。 (有少量的一次性物品不需要处理。例如,任务。但作为一般规则,如果是一次性的,请将其丢弃。)
我的问题不再那么多,何时。
完成后只处理一件事。不是之前,也不是之后。
我什么时候应该在.NET中处理我的对象?
在实现IDisposable时处理对象,并且您已完成使用它们。
我怎么知道某件事是否属于我必须处理的范畴?
实施IDisposable时。
我只是不知道我创建的对象或事物需要我负责处理。
一次性用品。
大多数经验丰富且专业的开发人员都知道他们创建的东西何时需要处理。我不明白如何知道这一点。
他们检查物体是否是一次性的。如果是,他们会处置它。
Dispose的目的是释放非托管资源"。好的,但我的问题是如何通过查看它是非托管资源的东西来了解?
它实现了IDisposable。
我不明白如何使用这种类型的解释来分类我需要处理的东西()和我不需要的东西。 .net框架中有各种各样的东西;如何分离出需要我处理它们的东西?我该怎么看才能告诉我,我对此负责?
检查它是否是IDisposable。
在那之后,答案继续谈论如何处置(),但我仍然停留在需要处理的东西上。
任何实现IDisposable的东西都需要处理。
我的问题是什么时候,意味着什么需要处理(),而不是我完成它。我知道,当我完成任务时,我不知道在我完成这些事情时我应该对哪些事情负责。
实现IDisposable的东西。
我希望有一个必须处理的一般原则,而不是一长串特定项目,对于像我这样寻求简单指导的人来说,这些项目并不是特别有用。
简单的指导原则是你应该丢弃一次性物品。
同样,我认为记忆释放很重要,而且很多经验和专业知识都用于学习为什么和如何,但我仍然在努力去理解需要处理什么。一旦我理解了我必须处理的东西(),那么我就可以开始学习如何去做了。
通过调用Dispose()来处理实现IDisposable的东西。
这还是一个不好的问题吗?
这是一个非常重复的问题。
你的耐心是一种善意。
感谢你在幽默中采取这个有点愚蠢的答案!
WRT范围!=生命周期&变量!=对象,非常有帮助。
这些通常很混乱,而且大多数时候,它几乎没有什么区别。但我发现,那些正在努力理解一个概念的人通常并不是模糊和不精确。
在VS中它是如此简单,只需在对象浏览器/智能感知中查看该对象是否包含Dispose()?
绝大多数时候,是的。
有一些不起眼的角落案件。正如我已经提到的,TPL团队所获得的智慧是,处理Task对象不仅是不必要的,而且可能适得其反。
还有一些类型实现了IDisposable,但使用了"显式接口实现"制作" Dispose"方法只能通过强制转换为IDisposable来访问。在大多数情况下,对象本身存在Dispose的同义词,通常称为" Close"或某些这样的事情。我不太喜欢这种模式,但有些人会使用它。
对于这些对象,using
块仍然有效。如果由于某种原因你想在不使用using
的情况下明确处理这些对象,那么(1)调用"关闭"方法,或任何它被称为,或(2)投射到IDisposable并处置它。
一般的智慧是:如果物品是一次性的,那么处理它并不会有害,当你完成它时这是一个很好的做法。
原因是:一次性物品通常代表稀缺的共享资源。例如,某个文件可能会以某种模式打开,该模式在您打开文件时拒绝其他进程访问该文件的权限。这是礼貌的事情,以确保文件在您完成后立即关闭。如果一个进程想要使用一个文件,那么很快就会有很多优势。
或者一次性可能代表图形对象。如果一个进程中有超过一万个活动对象,操作系统将停止发出新的图形对象,因此当您完成它们时,您必须让它们离开。
WRT实施IDisposable @ Brian的评论表明" normal"编码我可能不需要。如果我的班级不受管理,我会这样做吗?
好问题。您应该在两种情况下实现IDisposable。
(1)常见的场景:你正在编写一个长时间保留在另一个IDisposable对象上的对象,以及" inner"对象与"外部"的生命周期相同对象
例如:您正在实现一个记录器类,该类打开一个日志文件并在日志关闭之前保持打开状态。现在你有一个固定一次性的类,所以它本身也应该是一次性的。
我注意到在这种情况下不需要"外部"对象是 finalizable 。只是一次性的如果由于某种原因从未在外部对象上调用dispose,则内部对象的终结器将负责完成。
(2)罕见的场景:您正在实现一个新类,它要求操作系统或其他外部实体提供必须积极清理的资源,并且该资源的生命周期与对象的生命周期相同抓住它。
在这种非常罕见的情况下,您首先应该问自己是否有任何方法可以避免它。对于初学者到中级程序员来说,这是一个糟糕的情况。你真的需要了解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/
你没有问的问题,但我会回答:
我是否应该不实施IDisposable?
是。许多人在希望拥有具有以下语义的编码模式时实现IDisposable:
因此,例如,"冒充管理员,执行一些管理任务,恢复普通用户"。或者"开始处理事件,在事件发生时做事,停止处理事件"。或者"创建一个内存中的错误跟踪器,做一些可能会出错的东西,停止跟踪错误"。等等。你得到了一般模式。
这不适合一次性模式,但这并不能阻止人们编写代表任何非托管资源的类,但仍然可以实现IDisposable,就像它们一样。
这种观点使我成为少数;滥用这种机制,很多人都没有任何问题。但是,当我看到一个一次性的时候,我想"这个班级的作者希望我礼貌并在我自己之后清理我好的并且准备好了。&# 34;但是该类的实际合同通常是"您必须将其置于程序中的特定点,如果不这样,那么其余的程序逻辑在你这样做之前会是错的。这不是我期望在看到一次性用品时必须实施的合同。我希望我必须尽心尽力地清理资源。
答案 1 :(得分:2)
如果您有一个实现IDisposable
的对象,那么您必须始终在该对象上显式调用.Dispose()
。如果它没有实现IDisposable
那么显然你不会打电话给.Dispose()
,因为你不能。
如果您正在编写自己的对象,那么关于是否实现IDisposable
的规则就是:如果您的对象持有对非托管对象的引用,或者它是否包含对实现{{1}的对象的引用然后它必须实现IDisposable
。
GC 从不为您调用IDisposable
。你必须经常这样做 - 直接或通过终结者。
GC 可能(最有可能,但并非总是)调用终结器,因此您可以编写终结器来调用dispose,但要注意正确实现一次性模式,并确保你知道终结器可能永远不会运行所以如果你的dispose方法做了一些非常重要的事情,那么在你失去对象的引用之前直接调用.Dispose()
可能会更好。
答案 2 :(得分:1)
垃圾收集器(GC)保证托管 内存资源不再使用在内存限制之前发布达到
让我们打破这一点:
托管:松散地,这意味着资源完全在.NET / CLR中。例如,由非.NET C ++库分配的内存不是由GC发布的 。
内存:GC仅提供内存使用保证。存在许多其他资源类型,例如文件句柄。 GC没有逻辑来确保方便地释放文件句柄。
不再使用:这意味着所有引用该内存的变量都已终止其生命周期。正如Eric Lippert解释的那样,终身!=范围。
在达到内存限制之前:GC监视"内存压力"并确保在需要时释放内存。它使用一堆算法来决定何时最有效地执行此操作,但重要的是要注意GC决定何时自行释放资源(不确定地发布到您的程序)。
当依赖GC时,我们有几种情况:
在任何这些情况下,对象都应该实现IDisposable
来处理资源清理。
必须通过调用IDisposable
或使用Dispose
块(负责为您调用using
)来清除实例化Dispose
的任何实例化对象。< / p>
答案 3 :(得分:-1)
Dispose模式可用于清除托管和非托管资源。如果您的班级中有非托管资源,根据proper IDisposable implementation,您必须同时拥有Dispose和Finalize方法。
我如何知道GC知道/不知道什么?
GC仅对托管对象了解/感兴趣。 GC应该清除那些没有任何强引用的对象。它不知道你的逻辑。一个简单而明显的例子。
假设您有一个持续很长时间的MainView实例,并且您创建另一个订阅MainView实例中的事件的LittleView。
然后关闭LittleView,它就会消失。你知道你不再需要那个LittleView实例了。但是GC不知道你是否还需要LittleView,因为MainWindow有一个活动的事件订阅。
因此,GC不会费心从内存中清除LittleView实例。因此,您应该做的是在关闭视图时取消订阅该事件。然后GC知道没有对LittleView的强引用,并且它是可以访问的。
该帖子还使管理资源可能包含的内容变得复杂 非托管资源。哇。这比我最初要深刻得多 可想而知的。我仍然希望能够轻松地认识到如何知道需求 被处置。是这样的情况这是一个复杂的列表w / 条件和背景?
这是正确的,托管对象也可以拥有非托管资源。所有这些托管对象都有其finalize方法来清除非托管资源。
想知道为什么除Dispose外还需要finalize方法?
非托管资源可能会造成最危险的内存泄漏,因为在重新启动PC之前,内存泄漏可能会占用内存。所以,他们非常糟糕。
假设存在托管实例InsA,其中一些是非托管的。 InsA以一种清除非托管资源的方式实现了Dispose方法。但是如果你没有/忘记调用Dispose方法会发生什么?它永远不会清除非托管内存。这就是Finalization进入功能的原因。因此,如果您忘记/不调用Dispose,则最终确定将以释放非托管资源的方式执行Dispose。