C # 2008
我已经在这方面工作了一段时间,但我仍然对在代码中使用finalize和dispose方法感到困惑。我的问题如下:
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);
}
}
关于源代码的问题:
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 my NoGateway class. Because WebClient implements the IDisposable interface, does this mean that WebClient indirectly uses unmanaged resources? Is there a hard and fast rule to follow this? How do I know that a class uses unmanaged resources?
我同意pm100的观点(我应该在之前的文章中明确地说明这一点)。
除非需要,否则永远不要在类中实现IDisposable。具体来说,大约有5次你需要/应该实现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...
对于使用COM对象并且必须使用Marshal.ReleaseComObject(),推荐的替代方法是使用System.Runtime.InteropServices.SafeHandle类。
BCL(基类库团队)在这里有一篇关于它的很好的博客文章http://blogs.msdn.com/bclteam/archive/2005/03/16/396900.aspx
一个非常重要的注意事项是,如果你正在使用WCF并清理资源,你几乎总是应该避免使用'using'块。有很多博客和MSDN上的文章都在解释为什么这是一个坏主意。我也在这里发布了关于它的帖子-不要在WCF代理中使用'using()'
我同意pm100的观点(我应该在之前的文章中明确地说明这一点)。
除非需要,否则永远不要在类中实现IDisposable。具体来说,大约有5次你需要/应该实现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...
对于使用COM对象并且必须使用Marshal.ReleaseComObject(),推荐的替代方法是使用System.Runtime.InteropServices.SafeHandle类。
BCL(基类库团队)在这里有一篇关于它的很好的博客文章http://blogs.msdn.com/bclteam/archive/2005/03/16/396900.aspx
一个非常重要的注意事项是,如果你正在使用WCF并清理资源,你几乎总是应该避免使用'using'块。有很多博客和MSDN上的文章都在解释为什么这是一个坏主意。我也在这里发布了关于它的帖子-不要在WCF代理中使用'using()'
推荐的IDisposable模式如下。当编程一个使用IDisposable的类时,通常你应该使用两种模式:
当实现一个不使用非托管资源的密封类时,你只需像普通接口实现一样实现Dispose方法:
public sealed class A : IDisposable
{
public void Dispose()
{
// get rid of managed resources, call Dispose on member variables...
}
}
当实现一个非密封类时,像这样做:
public class B : IDisposable
{
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
// get rid of managed resources
}
// get rid of unmanaged resources
}
// only if you use unmanaged resources directly in B
//~B()
//{
// Dispose(false);
//}
}
注意,我没有在B中声明终结式;只有在有实际的非托管资源要处理时,才应该实现终结器。CLR处理可终结对象与处理不可终结对象是不同的,即使调用了SuppressFinalize。
所以,除非必须,否则你不应该声明终结器,但是你可以给类的继承者一个钩子来调用你的Dispose,如果他们直接使用非托管资源,他们自己实现终结器:
public class C : B
{
private IntPtr m_Handle;
protected override void Dispose(bool disposing)
{
if (disposing)
{
// get rid of managed resources
}
ReleaseHandle(m_Handle);
base.Dispose(disposing);
}
~C() {
Dispose(false);
}
}
如果您没有直接使用非托管资源(SafeHandle和friend不计算在内,因为它们声明了自己的终结器),那么就不要实现终结器,因为GC以不同的方式处理可终结类,即使您稍后抑制了终结器。还要注意,即使B没有终结器,它仍然调用SuppressFinalize来正确处理任何实现了终结器的子类。
当一个类实现IDisposable接口时,这意味着在某个地方有一些非托管资源,当您使用完该类时,这些资源应该被删除。实际资源封装在类中;您不需要显式地删除它们。只需调用Dispose()或将类包装在using(…){}中,就可以确保在必要时删除任何非托管资源。
实现IDisposable的官方模式很难理解。我认为这个更好:
public class BetterDisposableClass : IDisposable {
public void Dispose() {
CleanUpManagedResources();
CleanUpNativeResources();
GC.SuppressFinalize(this);
}
protected virtual void CleanUpManagedResources() {
// ...
}
protected virtual void CleanUpNativeResources() {
// ...
}
~BetterDisposableClass() {
CleanUpNativeResources();
}
}
一个更好的解决方案是有一个规则,你总是必须为你需要处理的任何非托管资源创建一个包装器类:
public class NativeDisposable : IDisposable {
public void Dispose() {
CleanUpNativeResource();
GC.SuppressFinalize(this);
}
protected virtual void CleanUpNativeResource() {
// ...
}
~NativeDisposable() {
CleanUpNativeResource();
}
}
对于SafeHandle及其衍生品,这些类应该非常少。
对于不直接处理非托管资源(即使存在继承)的一次性类,其结果是强大的:它们不再需要关心非托管资源。它们很容易实现和理解:
public class ManagedDisposable : IDisposable {
public virtual void Dispose() {
// dispose of managed resources
}
}