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?


当前回答

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: Know you will have scarce resources in your objects soon, just not now (and I mean that as in "we're still developing, it will be here before we're done", not as in "I think we'll need this") Using scarce resources 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.

其他回答

using(NoGateway objNoGateway = new NoGateway())

等于

try
{
    NoGateway = new NoGateway();
}

finally
{
    NoGateway.Dispose();
}

在GC销毁对象时调用终结器。这可能与你离开方法时完全不同。Dispose of IDisposable在你离开using块后立即被调用。因此,这种模式通常是在您不再需要资源后立即使用using来释放它们。

另一个答案的某些方面有点不正确,原因有二:

首先,

using(NoGateway objNoGateway = new NoGateway())

Actually相当于:

try
{
    NoGateway = new NoGateway();
}
finally
{
    if(NoGateway != null)
    {
        NoGateway.Dispose();
    }
}

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)

using(IDisposable objNoGateway = new NoGateway() as IDisposable)
{
    if (NoGateway != null)
    {
        ...

如果'as'操作符返回null(或返回资源的属性或方法),并且'using'块中的代码可以防止'null',那么在尝试对空对象调用Dispose时,您的代码不会因为'内置'空检查而崩溃。

你的回复不准确的第二个原因是由于以下stmt:

在GC销毁对象时调用终结器

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().

我已经在我的博客上发布了Dispose模式的正确实现:如何正确地实现Dispose模式

1) WebClient是一个托管类型,所以你不需要终结器。如果您的用户不Dispose()您的NoGateway类,并且本机类型(不被GC收集)之后需要清理,则需要结束器。在这种情况下,如果用户没有调用Dispose(),所包含的WebClient将在NoGateway调用之后被GC丢弃。

间接地,是的,但你不必担心。你的代码是正确的,你不能防止你的用户很容易忘记Dispose()。

来自msdn的模式

public class BaseResource: IDisposable
{
   private IntPtr handle;
   private Component Components;
   private bool disposed = false;
   public BaseResource()
   {
   }
   public void Dispose()
   {
      Dispose(true);      
      GC.SuppressFinalize(this);
   }
   protected virtual void Dispose(bool disposing)
   {
      if(!this.disposed)
      {        
         if(disposing)
         {
            Components.Dispose();
         }         
         CloseHandle(handle);
         handle = IntPtr.Zero;
       }
      disposed = true;         
   }
   ~BaseResource()      
   {      Dispose(false);
   }
   public void DoSomething()
   {
      if(this.disposed)
      {
         throw new ObjectDisposedException();
      }
   }
}
public class MyResourceWrapper: BaseResource
{
   private ManagedResource addedManaged;
   private NativeResource addedNative;
   private bool disposed = false;
   public MyResourceWrapper()
   {
   }
   protected override void Dispose(bool disposing)
   {
      if(!this.disposed)
      {
         try
         {
            if(disposing)
            {             
               addedManaged.Dispose();         
            }
            CloseHandle(addedNative);
            this.disposed = true;
         }
         finally
         {
            base.Dispose(disposing);
         }
      }
   }
}

没有人回答你是否应该实现IDisposable的问题,即使你不需要它。

简单的回答:不

长一点的回答:

这将允许你的类的消费者使用“using”。我想问的问题是,他们为什么要这么做?大多数开发者不会使用“使用”,除非他们知道他们必须这么做——他们是怎么知道的。要么

很明显,从经验来看(例如,一个套接字类) 它的记录 他们很谨慎,可以看到类实现了IDisposable

因此,通过实现IDisposable,你可以告诉开发人员(至少是一些开发人员)这个类封装了一些必须发布的东西。他们会使用“using”——但在其他情况下,使用是不可能的(对象的作用域不是本地的);他们将不得不开始担心在其他情况下对象的生命周期-我肯定会担心。但这是不必要的

你实现Idisposable,让他们使用使用,但他们不会使用使用,除非你告诉他们。

所以不要这么做