Dispose 方法做了多少工作?
Dispose 方法中应该完成多少工作?在构造函数中,我始终采取的立场是,您应该只执行实例化对象绝对必要的操作。在这种情况下,我也一直采取这样的方法:您应该只在处置时清理开放资源。关闭文件、释放内存、处置子一次性对象等。您不应该在 Dispose 方法中执行冗长的过程,例如触摸文件、访问数据库等。
我错了吗?只要您处理任何可能的异常,这样它们就不会从方法中冒出来,这些操作就可以吗?我只是认为在 Dispose 中做太多事情并不是一个好主意。我想知道社区的想法。
How much work should be done in a Dispose method? In constructors I've always taken the stance that you should only do what is absolutely necessary to instantiate the object. This being the case I've also always taken the approach that you should ONLY be cleaning up open resources when disposing. Closing files, freeing memory, disposing of child disposable object, etc. You shouldn't be doing lengthy processes like touching files, accessing databases and such in the Dispose method.
Am I wrong? Are those action's OK as long as you are handling any possible exceptions so they don't bubble out of the method? I just don't think doing a lot in Dispose is a good idea. I would like to know what the community thinks.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
不,你是对的。通常,Dispose 方法用于清理类可能已分配的非托管资源。
但这很难一概而论。在某些情况下,Dispose 方法只是用于确保某些操作的执行。例如,在 ASP.NET MVC 中,有一个 Html.BeginForm 帮助器,其使用方式如下:
Dispose 方法所做的就是呈现一个结束
标记。正如你所看到的,人们可以对这种模式进行创造性的设计,并且在没有特定场景的情况下很难得出结论。
但在最常见的情况下,它用于释放非托管资源。
No, you are right. In general the Dispose method is used to clean the unmanaged resources that your class might have allocated.
But that's difficult to generalize. There are cases where the Dispose method is simply used to ensure that some operation executes. For example in ASP.NET MVC there's the Html.BeginForm helper which is used like that:
and all that the Dispose method does is render a closing
</form>
tag. So as you can see people could be creative with the pattern and it is very difficult to draw conclusions without a specific scenario.But in the most common situations it's used to release unmanaged resources.
“这取决于”。我们谈论的是哪种数据库/文件访问?举例来说,您的一次性对象是某种记录器,并且您按照以下模式使用它,
我认为记录器在 Dispose 的构造函数“日志已完成”中写出“日志已开始”是完全可以接受的。
"It depends". What kind of database/file access are we talking about? Say for example that your disposable object is some sort of logger and you use it in the following pattern
I think it would be perfectly acceptable for logger to write out "Log started" in the constructor "Log Completed" in Dispose.
这取决于,您实现 IDispose 接口只是为了方便“using”语句,还是实现完整的 IDisposable 模式?在后一种完全一次性模式的情况下,只要您的“处置”参数为 true(即您不在 GC 中),执行更复杂的操作仍然是可以接受的。
当您定义一个调用 Dispose 方法的终结器时,实际上没有什么需要担心的。其他已经提到的 IDisposable 接口的类似使用/滥用(即
using (Html.BeginForm())
)能够执行任何操作。通常这可以大大降低代码复杂性并防止编码人员意外忘记执行某些关闭操作。一个向下(或向上)的一面是 代码执行在finally 块中略有不同。恕我直言,对象应该在构建后有效。因此,如果您需要做很多工作来构建某些东西,那就这样吧。不要考虑所涉及的工作量,而要考虑对象的消费者及其可用性。构造后的 Initialize() 方法很糟糕;)
实际上让我们稍微分解一下...
从 GC 调用到 Finalizer 的处理
当你实现 IDisposable 模式(不是接口、模式、终结器等等)时,你本质上是在说你的对象拥有其他人不知道的非托管资源。这意味着您已经 PInvoked 调用了 Win32 的 CreateFile,或者您可能调用了 Marshal.AllocHGlobal 或类似的东西。本质上,您可能有一个 IntPtr 实例成员,您需要对其执行某些操作以防止内存泄漏。这些是当处置参数为 false 时应该执行的唯一类型的操作(即从 GC 线程上的终结器调用)。
通常,您不会对子项调用 Dispose 方法。您不应期望任何子对象都是有效的。只需触摸子对象的成员可能会意外“复活”或 复活它。
因此,当您编写在 Finalizer 调用的 Dispose 方法中执行的代码时,您必须小心。您正在 GC 线程上执行,而应用程序的其余部分正在等待您。您应该执行尽可能少的操作来释放非托管内存/资源并退出。切勿抛出异常,如果您正在调用可能抛出异常的 API,则应捕获引发的任何异常。将异常传播回 GC 将过早中止终结器线程,并且要终结的剩余对象将没有机会清理。
通过 IDisposable.Dispose() 方法进行处置
正如我已经说过的,使用 Dispose 方法足够安全,可以安全地容纳任意数量的代码/进程。您可以在此处释放非托管资源、调用子对象的 dispose 方法、刷新和关闭文件等。我编写的大多数 Dispose 方法没有关联的 Finalizer,因此不遵循 IDisposable 模式,但它们实现 IDisposable 只是为了方便
using
语句。当从终结器中使用有问题的 dispose 方法时,您是绝对正确的。您关于在 Dispose 方法中应该做什么和不应该做什么的断言实际上应该重新措辞以适用于 Finalizer 调用的任何内容。事实上,这通常是在名为 Dispose 的方法中完成的,这是一个约定问题,即 IDisposable 模式,但这些问题很容易存在于 Finalizer 使用的其他方法中。
This depends, are you implementing the IDispose interface just for the convenience of a 'using' statement, or are you implementing the full IDisposable pattern? In the later case of a full disposable pattern it is still acceptable to perform more complex actions provided that you're 'disposing' parameter is true (i.e. you are not in GC).
When you are defining a finalizer that calls the Dispose method there really is not too much to be concerned about. Similar uses/abuses of the IDisposable interface mentioned already be others (i.e.
using (Html.BeginForm())
) are capable of performing any action. Often this can greatly reduce code complexity and prevent coders from accidentally forgetting to perform some closing action. One down (or up) side to this is that code executes a little differently inside a finally block.Objects, IMHO, should be valid post construction. So if you have a lot of work to do to construct something so be it. Don't think of the workload involved, think of the consumer of you're object and it's usability. Post-construction Initialize() methods suck ;)
Actually let's break this down a bit...
Disposing from the GC call to the Finalizer
When you implement the IDisposable pattern (not the interface, the pattern, finalizer and all) you are essentially saying that your object has an unmanaged resource that nobody else knows about. That means you have PInvoked a call to Win32's CreateFile, or maybe you called Marshal.AllocHGlobal or something like that. Essentially you likely have an IntPtr instance member you need to do something with to prevent a memory leak. These are the ONLY types of things that should be done when the disposing parameter is false (i.e. called from the finalizer on the GC thread).
Generally you DO NOT call the Dispose method on children. You should not expect any child object to be valid. Simply touching a member of the child object can accidentally 'revive' or resurrect it.
So when you are writing code that executes in a Dispose method called from the Finalizer you have to be careful. You are executing on the GC thread while the rest of your application waits for you. You should perform as few operations as possible to release the unmanaged memory/resource and quit. Never throw an exception and if you are calling an API that may throw you should catch any exception raised. Propagating exceptions back to the GC will prematurely abort the finalizer thread and the remaining objects to be finalized will not have a chance to clean up.
Disposing from the IDisposable.Dispose() method
As I’ve already said, using the Dispose method is safe enough and can safely accommodate any amount of code/process. This is where you would free unmanaged resources, call the dispose method of child objects, flush and close files, etc. Most of the Dispose methods I’ve written do not have an associated Finalizer and therefore do not follow the IDisposable pattern, yet they implement IDisposable just for the convenience of the
using
statement.You are absolutely right when the dispose method in question is used from a finalizer. You’re assertions about what you should and should not do in a Dispose method should actually be reworded to apply to anything called by a Finalizer. The fact that this is generally done in a method called Dispose is a matter of convention, the IDisposable pattern, but these issues could easily exist in other methods used by the Finalizer.
如果一个对象对某些外部实体的状态做了一些事情,使它们对此更有用,但对其其他人不太有用,则该对象的 Dispose 方法应该执行任何必要的操作来恢复外部这些实体处于更普遍有用的状态。如果希望避免对象在
Dispose
中执行过多工作,则应该设计该对象,以便永远不要让任何外部实体处于难以清理的状态。顺便说一句,微软喜欢使用“非托管资源”这个术语,并给出了例子,但从来没有真正提供一个好的定义。我建议,如果外部实体以不利于其他对象或实体的方式代表该对象改变其行为,并且该外部实体将继续改变其行为,直到该物体阻止它这样做。
If an object does something to the state of some outside entities in a way which makes them more useful to that but less useful to everyone else, the
Dispose
method of the object should do whatever is necessary to restore outside those entities to more generally-useful state. If one wishes to avoid having an object do too much work inDispose
, one should design the object so as never leave any outside entities in a state which would be onerous to clean up.BTW, Microsoft likes to use the term "unmanaged resources", and gives examples, but never really offers a good definition. I would suggest that an object holds an "unmanaged resource" if an outside entity is altering its behavior on behalf of that object, in a fashion which is detrimental to other objects or entities, and if that outside entity will continue to alter its behavior until the object stops it from doing so.
你应该倾向于你已经得出的结论。但是,在某些情况下,您需要确保服务已停止,这可能包括记录服务关闭的消息或将当前运行时状态保存到数据存储等内容。这种类型的处置通常仅适用于具有应用程序范围生活方式的事物,这意味着它们在应用程序运行的整个过程中都存在。因此,存在超出预期正常情况的情况。就像编写代码时应遵循的每条规则一样。
You should lean toward the conclusion you have already come to. However there are situations where you need to ensure that services are stopped and that could include things like messages being logged for the service shutdown, or saving the current runtime state to data store. This type of disposal usually only applies to things that have a lifestyle that is application scope, meaning they exist the whole time the application is running. So there are situations outside of the expected norm. As is the case with every rule you should follow when writing code.