C#中Finalize/Dispose方法的使用
C# 2008
我已经在这方面工作了一段时间了,但我仍然对代码中 Finalize 和 dispose 方法的使用感到困惑。 我的问题如下:
我知道我们在处理非托管资源时只需要一个终结器。 但是,如果有托管资源调用非托管资源,是否还需要实现终结器?
但是,如果我开发一个不直接或间接使用任何非托管资源的类,我是否应该实现
IDisposable
以允许该类的客户端使用“using 语句”?实现 IDisposable 只是为了让您的类的客户端能够使用 using 语句是否可行?
using(myClass objClass = new myClass()) { // 在这里做一些事情 }
我开发了下面这个简单的代码来演示 Finalize/dispose 的使用:
公共类NoGateway:IDisposable { 私有WebClient wc = null; 公共NoGateway() { wc = new WebClient(); wc.DownloadStringCompleted += wc_DownloadStringCompleted; } // 启动异步调用以查找 NoGateway 是 true 还是 false 公共无效NoGatewayStatus() { // 开始异步下载 // 在这里做其他工作 wc.DownloadStringAsync(new Uri(www.xxxx.xxx)); } 私有无效 wc_DownloadStringCompleted(对象发送者,DownloadStringCompletedEventArgs e) { // 在这里工作 } // 释放 NoGateway 对象 公共无效处置() { wc.DownloadStringCompleted -= wc_DownloadStringCompleted; wc.Dispose(); GC.SuppressFinalize(this); } }
关于源代码的问题:
这里我没有添加终结器,通常终结器会被GC调用,终结器将调用 Dispose。 由于我没有终结器,我什么时候调用 Dispose 方法? 是不是该类的客户端必须调用它?
所以我在示例中的类称为 NoGateway,客户端可以像这样使用和处置该类:
using(NoGateway objNoGateway = new NoGateway()) { // 在这里做一些事情 }
当执行到using块末尾时,Dispose方法会自动调用吗?还是客户端必须手动调用dispose方法? 即
NoGateway objNoGateway = new NoGateway(); // 对对象做一些事情 objNoGateway.Dispose(); // 完成了
我在我的
NoGateway
类中使用WebClient
类。 因为WebClient
实现了IDisposable
接口,这是否意味着WebClient
间接使用非托管资源? 有没有硬性规定可以遵循? 我如何知道某个类使用非托管资源?
C# 2008
I have been working on this for a while now, and I am still confused about the use of finalize and dispose methods in code. My questions are below:
I know that we only need a finalizer while disposing unmanaged resources. However, if there are managed resources that make calls to unmanaged resources, would it still need to implement a finalizer?
However, if I develop a class that doesn't use any unmanaged resource - directly or indirectly, should I implement the
IDisposable
to allow the clients of that class to use the 'using statement'?Would it be feasible to implement IDisposable just to enable clients of your class to use the using statement?
using(myClass objClass = new myClass()) { // Do stuff here }
I have developed this simple code below to demonstrate the Finalize/dispose use:
public class NoGateway : IDisposable { private WebClient wc = null; public NoGateway() { wc = new WebClient(); wc.DownloadStringCompleted += wc_DownloadStringCompleted; } // Start the Async call to find if NoGateway is true or false public void NoGatewayStatus() { // Start the Async's download // Do other work here wc.DownloadStringAsync(new Uri(www.xxxx.xxx)); } private void wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e) { // Do work here } // Dispose of the NoGateway object public void Dispose() { wc.DownloadStringCompleted -= wc_DownloadStringCompleted; wc.Dispose(); GC.SuppressFinalize(this); } }
Question about the source code:
Here I have not added the finalizer, and normally the finalizer will be called by the GC, and the finalizer will call the Dispose. As I don't have the finalizer, when do I call the Dispose method? Is it the client of the class that has to call it?
So my class in the example is called NoGateway and the client could use and dispose of the class like this:
using(NoGateway objNoGateway = new NoGateway()) { // Do stuff here }
Would the Dispose method be automatically called when execution reaches the end of the using block, or does the client have to manually call the dispose method? i.e.
NoGateway objNoGateway = new NoGateway(); // Do stuff with object objNoGateway.Dispose(); // finished with it
I am using the
WebClient
class in myNoGateway
class. BecauseWebClient
implements theIDisposable
interface, does this mean thatWebClient
indirectly uses unmanaged resources? Is there a hard and fast rule to follow this? How do I know that a class uses unmanaged resources?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(13)
据我所知,强烈建议不要使用终结器/析构函数:
大多数情况下,这是因为不知道何时或是否会调用它。 处理方法要好得多,特别是如果您直接使用或处理的话。
使用效果很好。 用它 :)
From what I know, it's highly recommended NOT to use the Finalizer / Destructor:
Mostly, this is due to not knowing when or IF it will be called. The dispose method is much better, especially if you us using or dispose directly.
using is good. use it :)
推荐的 IDisposable 模式位于此处。 在编写使用 IDisposable 的类时,通常应该使用两种模式:
当实现不使用非托管资源的密封类时,只需像普通接口实现一样实现 Dispose 方法:
当实现非密封类时,请像这样执行:
请注意,我没有在
B
中声明终结器; 仅当您有实际的非托管资源需要处置时,您才应该实现终结器。 即使调用了SuppressFinalize
,CLR 处理可终结对象的方式也与处理不可终结对象的方式不同。因此,除非必要,否则不应声明终结器,但如果类的继承者直接使用非托管资源,则可以为类的继承者提供一个钩子来调用
Dispose
并自行实现终结器:如果您是不直接使用非托管资源(
SafeHandle
和朋友不算数,因为他们声明了自己的终结器),那么不要实现终结器,因为 GC 以不同的方式处理可终结的类,即使您稍后抑制终结器。 另请注意,即使B
没有终结器,它仍然调用SuppressFinalize
来正确处理任何实现终结器的子类。当一个类实现 IDisposable 接口时,这意味着某个地方有一些非托管资源,当您使用完该类时应该将其删除。 实际资源封装在类中; 您不需要显式删除它们。 只需调用
Dispose()
或将类包装在using(...) {}
中即可确保根据需要删除任何非托管资源。The recommended IDisposable pattern is here. When programming a class that uses IDisposable, generally you should use two patterns:
When implementing a sealed class that doesn't use unmanaged resources, you simply implement a Dispose method as with normal interface implementations:
When implementing an unsealed class, do it like this:
Notice that I haven't declared a finalizer in
B
; you should only implement a finalizer if you have actual unmanaged resources to dispose. The CLR deals with finalizable objects differently to non-finalizable objects, even ifSuppressFinalize
is called.So, you shouldn't declare a finalizer unless you have to, but you give inheritors of your class a hook to call your
Dispose
and implement a finalizer themselves if they use unmanaged resources directly:If you're not using unmanaged resources directly (
SafeHandle
and friends doesn't count, as they declare their own finalizers), then don't implement a finalizer, as the GC deals with finalizable classes differently, even if you later suppress the finalizer. Also note that, even thoughB
doesn't have a finalizer, it still callsSuppressFinalize
to correctly deal with any subclasses that do implement a finalizer.When a class implements the IDisposable interface, it means that somewhere there are some unmanaged resources that should be got rid of when you've finished using the class. The actual resources are encapsulated within the classes; you don't need to explicitly delete them. Simply calling
Dispose()
or wrapping the class in ausing(...) {}
will make sure any unmanaged resources are got rid of as necessary.实现 IDisposable 的官方模式很难理解。 我相信这个更好:
一个更好的解决方案是制定一条规则,您始终必须遵守为需要处理的任何非托管资源创建一个包装类:
使用
SafeHandle
及其派生类,这些类应该非常罕见。即使存在继承,不直接处理非托管资源的一次性类的结果也是强大的:它们不再需要关心非托管资源。 它们的实施和理解简单:
The official pattern to implement
IDisposable
is hard to understand. I believe this one is better:An even better solution is to have a rule that you always have to create a wrapper class for any unmanaged resource that you need to handle:
With
SafeHandle
and its derivatives, these classes should be very rare.The result for disposable classes that don't deal directly with unmanaged resources, even in the presence of inheritance, is powerful: they don't need to be concerned with unmanaged resources anymore. They'll be simple to implement and to understand:
请注意,任何 IDisposable 实现都应遵循以下模式(恕我直言)。 我根据几位优秀的 .NET“大神”的信息开发了这个模式 .NET Framework 设计指南 (请注意,由于某种原因,MSDN 不遵循这一点!)。 .NET Framework 设计指南由 Krzysztof Cwalina(当时的 CLR 架构师)、Brad Abrams(我相信当时的 CLR 项目经理)和 Bill Wagner([Effective C#] 和 [More effective C#](只需看一下)编写。在 Amazon.com 上查找这些:
请注意,除非您的类直接包含(而不是继承)非托管资源,否则您永远不应该实现 Finalizer。 一旦您在类中实现了 Finalizer,即使它从未被调用,它也保证会存在。它会自动放置在终结队列中(在单个线程上运行)。此外,一个非常重要的注意事项...在终结器中执行的所有代码(如果您需要实现)必须是线程安全的并且异常安全!否则会发生不好的事情......(即,未确定的行为,在发生异常的情况下,致命的不可恢复的应用程序崩溃)
:
如下 在派生类中实现 IDisposable 的代码。 请注意,您不需要在派生类的定义中显式列出对 IDisposable 的继承。
我已将此实现发布在我的博客上: 如何正确实现 Dispose 模式
Note that any IDisposable implementation should follow the below pattern (IMHO). I developed this pattern based on info from several excellent .NET "gods" the .NET Framework Design Guidelines (note that MSDN does not follow this for some reason!). The .NET Framework Design Guidelines were written by Krzysztof Cwalina (CLR Architect at the time) and Brad Abrams (I believe the CLR Program Manager at the time) and Bill Wagner ([Effective C#] and [More Effective C#] (just take a look for these on Amazon.com:
Note that you should NEVER implement a Finalizer unless your class directly contains (not inherits) UNmanaged resources. Once you implement a Finalizer in a class, even if it is never called, it is guaranteed to live for an extra collection. It is automatically placed on the Finalization Queue (which runs on a single thread). Also, one very important note...all code executed within a Finalizer (should you need to implement one) MUST be thread-safe AND exception-safe! BAD things will happen otherwise...(i.e. undetermined behavior and in the case of an exception, a fatal unrecoverable application crash).
The pattern I've put together (and written a code snippet for) follows:
Here is the code for implementing IDisposable in a derived class. Note that you do not need to explicitly list inheritance from IDisposable in the definition of the derived class.
I've posted this implementation on my blog at: How to Properly Implement the Dispose Pattern
我同意 pm100 (并且应该在我之前的帖子中明确指出这一点)。
除非需要,否则永远不应该在类中实现 IDisposable。 具体来说,大约有 5 次您需要/应该实现 IDisposable:
您的类显式包含(即不是通过继承)任何实现 IDisposable 的托管资源,并且一旦您的类不再存在就应该清除用过的。 例如,如果您的类包含 Stream、DbCommand、DataTable 等的实例。
您的类显式包含实现 Close() 方法的任何托管资源 - 例如 IDataReader、IDbConnection 等。请注意,其中一些类请通过使用 Dispose() 以及 Close() 方法来实现 IDisposable。
您的类显式包含非托管资源 - 例如 COM 对象、指针(是的,您可以在托管 C# 中使用指针,但它们必须在“不安全”块中声明,等等。
对于非托管资源,还应确保在 RCW 上调用 System.Runtime.InteropServices.Marshal.ReleaseComObject()。 即使 RCW 理论上是一个托管包装器,但幕后仍然存在引用计数。
如果您的类使用强引用订阅事件。 您需要取消注册/脱离活动。 在尝试取消注册/分离它们之前,始终要确保它们不为空!。
您的类包含上述内容的任意组合...
处理 COM 对象并必须使用 Marshal.ReleaseComObject() 的推荐替代方法是使用System.Runtime.InteropServices.SafeHandle 类。
BCL(基类库团队)在这里有一篇关于它的很好的博客文章 http://blogs.msdn.com/bclteam/archive/2005/03/16/396900.aspx
需要注意的一个非常重要的事项是,如果您正在使用 WCF 并清理资源,您几乎应该始终避免“使用”块。 有很多博客文章以及 MSDN 上的一些文章都说明了为什么这是一个坏主意。 我也在这里发布了相关内容 - 不要将“using()”与 WCF 代理一起使用
I agree with pm100 (and should have explicitly said this in my earlier post).
You should never implement IDisposable in a class unless you need it. To be very specific, there are about 5 times when you would ever need/should implement IDisposable:
Your class explicitly contains (i.e. not via inheritance) any managed resources which implement IDisposable and should be cleaned up once your class is no longer used. For example, if your class contains an instance of a Stream, DbCommand, DataTable, etc.
Your class explicitly contains any managed resources which implement a Close() method - e.g. IDataReader, IDbConnection, etc. Note that some of these classes do implement IDisposable by having Dispose() as well as a Close() method.
Your class explicitly contains an unmanaged resource - e.g. a COM object, pointers (yes, you can use pointers in managed C# but they must be declared in 'unsafe' blocks, etc.
In the case of unmanaged resources, you should also make sure to call System.Runtime.InteropServices.Marshal.ReleaseComObject() on the RCW. Even though the RCW is, in theory, a managed wrapper, there is still reference counting going on under the covers.
If your class subscribes to events using strong references. You need to unregister/detach yourself from the events. Always to make sure these are not null first before trying to unregister/detach them!.
Your class contains any combination of the above...
A recommended alternative to working with COM objects and having to use Marshal.ReleaseComObject() is to use the System.Runtime.InteropServices.SafeHandle class.
The BCL (Base Class Library Team) has a good blog post about it here http://blogs.msdn.com/bclteam/archive/2005/03/16/396900.aspx
One very important note to make is that if you are working with WCF and cleaning up resources, you should ALMOST ALWAYS avoid the 'using' block. There are plenty of blog posts out there and some on MSDN about why this is a bad idea. I have also posted about it here - Don't use 'using()' with a WCF proxy
使用 lambda 代替 IDisposable。
我从来没有对整个使用/IDisposable 的想法感到兴奋。 问题是它要求调用者:
我的新首选方法是使用工厂方法和 lambda 代替
想象一下我想用 SqlConnection 做一些事情(应该将其包装在 using 中)。 传统上,您会采用
New way
在第一种情况下,调用者可以简单地不使用 using 语法。 在第二种情况下,用户别无选择。 没有创建 SqlConnection 对象的方法,调用者必须调用 DoWithConnection。
DoWithConnection 看起来这个
MakeConnection
现在是私有的Using lambdas instead of IDisposable.
I have never been thrilled with the whole using/IDisposable idea. The problem is that it requires the caller to:
My new preferred method is to use a factory method and a lambda instead
Imagine I want to do something with a SqlConnection (something that should be wrapped in a using). Classically you would do
New way
In the first case the caller could simply not use the using syntax. IN the second case the user has no choice. There is no method that creates a SqlConnection object, the caller must invoke DoWithConnection.
DoWithConnection looks like this
MakeConnection
is now private没有人回答关于您是否应该实现 IDisposable 的问题,即使您不需要它。
简短的回答:没有
长的回答:
这将允许您的班级的消费者使用“使用”。 我要问的问题是——他们为什么要这么做? 大多数开发人员不会使用“使用”,除非他们知道必须使用“使用”——以及他们如何知道。 要么
因此,通过实现 IDisposable,您告诉开发人员(至少是一些开发人员)该类包装了必须发布的东西。 他们会使用“using”——但在其他情况下,using 是不可能的(对象的范围不是本地的); 在其他情况下,他们将不得不开始担心对象的生命周期 - 我肯定会担心。 但这不是必需的。
您实现了 Idisposable 来使他们能够使用 using,但除非您告诉他们,否则他们不会使用 using。
所以不要这样做
nobody answered the question about whether you should implement IDisposable even though you dont need it.
Short answer : No
Long answer:
This would allow a consumer of your class to use 'using'. The question I would ask is - why would they do it? Most devs will not use 'using' unless they know that they must - and how do they know. Either
So by implementing IDisposable you are telling devs (at least some) that this class wraps up something that must be released. They will use 'using' - but there are other cases where using is not possible (the scope of object is not local); and they will have to start worrying about the lifetime of the objects in those other cases - I would worry for sure. But this is not necessary
You implement Idisposable to enable them to use using, but they wont use using unless you tell them to.
So dont do it
处置模式:
继承示例:
Dispose pattern:
Example of inheritance:
如果您使用的其他托管对象正在使用非托管资源,则您没有责任确保这些对象最终确定。 您的责任是,当在您的对象上调用 Dispose 时,对这些对象调用 Dispose,并在此停止。
如果你的类不使用任何稀缺资源,我不明白为什么你会让你的类实现 IDisposable。 仅当您符合以下条件时才应这样做:
是的,使用您的代码的代码必须调用对象的 Dispose 方法。 是的,使用您的对象的代码可以使用
using
,如您所示。(又是 2?)WebClient 很可能使用非托管资源或实现 IDisposable 的其他托管资源。 然而,确切的原因并不重要。 重要的是它实现了 IDisposable,因此您需要根据这些知识采取行动,在使用完该对象后处理该对象,即使事实证明 WebClient 根本不使用其他资源。
If you are using other managed objects that are using unmanaged resources, it is not your responsibility to ensure those are finalized. Your responsibility is to call Dispose on those objects when Dispose is called on your object, and it stops there.
If your class doesn't use any scarce resources, I fail to see why you would make your class implement IDisposable. You should only do so if you're:
Yes, the code that uses your code must call the Dispose method of your object. And yes, the code that uses your object can use
using
as you've shown.(2 again?) It is likely that the WebClient uses either unmanaged resources, or other managed resources that implement IDisposable. The exact reason, however, is not important. What is important is that it implements IDisposable, and so it falls on you to act upon that knowledge by disposing of the object when you're done with it, even if it turns out WebClient uses no other resources at all.
另一个答案的某些方面略有不正确,原因有两个:
首先,
实际上相当于:
这听起来可能很荒谬,因为“new”运算符永远不应该返回“null”,除非出现 OutOfMemory 异常。 但请考虑以下情况:
1. 您调用返回 IDisposable 资源的 FactoryClass 或
2. 如果您的类型可能继承也可能不继承 IDisposable,具体取决于其实现 - 请记住,我在许多客户端上多次看到 IDisposable 模式实现不正确,其中开发人员只是添加 Dispose() 方法而不继承 IDisposable (不好,不好,不好)。 您还可能会遇到从属性或方法返回 IDisposable 资源的情况(再次糟糕,糟糕,糟糕 - 不要“放弃您的 IDisposable 资源)”
如果“as”运算符返回 null(或者属性或方法返回资源),并且“using”块中的代码可以防止“null”,由于“内置”空检查,当尝试对空对象调用 Dispose 时,您的代码不会崩溃。
您的回复不准确的第二个原因是因为以下内容:
首先,终结(以及 GC 本身)是不确定的。 CLR 决定何时调用终结器。 即开发人员/代码不知道。 如果 IDisposable 模式正确实现(正如我上面发布的)并且 GC.SuppressFinalize() 已被调用,则 Finalizer 将不会被调用。 这是正确实施该模式的重要原因之一。 由于每个托管进程只有 1 个 Finalizer 线程,无论逻辑处理器的数量有多少,您都可以通过备份甚至通过忘记调用 GC.SuppressFinalize() 挂起 Finalizer 线程来轻松降低性能。
我在我的博客上发布了 Dispose 模式的正确实现: 如何正确实现 Dispose 模式
Some aspects of another answer are slightly incorrect for 2 reasons:
First,
actually is equivalent to:
This may sound ridiculous since the 'new' operator should never return 'null' unless you have an OutOfMemory exception. But consider the following cases:
1. You call a FactoryClass that returns an IDisposable resource or
2. If you have a type that may or may not inherit from IDisposable depending on its implementation - remember that I've seen the IDisposable pattern implemented incorrectly many times at many clients where developers just add a Dispose() method without inheriting from IDisposable (bad, bad, bad). You could also have the case of an IDisposable resource being returned from a property or method (again bad, bad, bad - don't 'give away your IDisposable resources)
If the 'as' operator returns null (or property or method returning the resource), and your code in the 'using' block protects against 'null', your code will not blow up when trying to call Dispose on a null object because of the 'built-in' null check.
The second reason your reply is not accurate is because of the following stmt:
First, Finalization (as well as GC itself) is non-deterministic. THe CLR determines when it will call a finalizer. i.e. the developer/code has no idea. If the IDisposable pattern is implemented correctly (as I've posted above) and GC.SuppressFinalize() has been called, the the Finalizer will NOT be called. This is one of the big reasons to properly implement the pattern correctly. Since there is only 1 Finalizer thread per managed process, regardless of the number of logical processors, you can easily degrade performance by backing up or even hanging the Finalizer thread by forgetting to call GC.SuppressFinalize().
I've posted a correct implementation of the Dispose Pattern on my blog: How to Properly Implement the Dispose Pattern
1) WebClient 是托管类型,因此不需要终结器。 如果您的用户不使用 NoGateway 类的 Dispose() 并且之后需要清理本机类型(不被 GC 收集),则需要终结器。 在这种情况下,如果用户不调用 Dispose(),则包含的 WebClient 将在 NoGateway 调用后立即由 GC 进行处理。
2)间接是的,但你不必担心。 您的代码是正确的,您无法阻止您的用户很容易忘记 Dispose() 。
1) WebClient is a managed type, so you don't need a finalizer. The finalizer is needed in the case your users don't Dispose() of your NoGateway class and the native type (which is not collected by the GC) needs to be cleaned up after. In this case, if the user doesn't call Dispose(), the contained WebClient will be disposed by the GC right after the NoGateway does.
2) Indirectly yes, but you shouldn't have to worry about it. Your code is correct as stands and you cannot prevent your users from forgetting to Dispose() very easily.
来自 msdn 的模式
Pattern from msdn
相当于
GC 销毁对象时调用终结器。 这可能是在与您离开系统时完全不同的时间。 IDisposable 的 Dispose 在您离开 using 块后立即调用。 因此,该模式通常是在不再需要资源后立即使用 using 来释放资源。
is equivalent to
A finalizer is called upon the GC destroying your object. This can be at a totally different time than when you leave your method. The Dispose of IDisposable is called immediately after you leave the using block. Hence the pattern is usually to use using to free ressources immediately after you don't need them anymore.