Use of Finalize/Dispose method in C#

后端 未结 13 2051
轮回少年
轮回少年 2020-11-21 23:20

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:

相关标签:
13条回答
  • 2020-11-21 23:40

    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.

    0 讨论(0)
  • 2020-11-21 23:41

    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:

    • know that they must use IDisposable
    • remember to use 'using'.

    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

    using (Var conn = Factory.MakeConnection())
    {
         conn.Query(....);
    }
    

    New way

    Factory.DoWithConnection((conn)=>
    {
        conn.Query(...);
    }
    

    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

    void DoWithConnection(Action<SqlConnection> action)
    {
       using (var conn = MakeConnection())
       {
           action(conn);
       }
    }
    

    MakeConnection is now private

    0 讨论(0)
  • 2020-11-21 23:42

    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:

    1. 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.

    2. 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.

    3. 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.

    4. 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!.

    5. 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

    0 讨论(0)
  • 2020-11-21 23:42

    Pattern from 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);
             }
          }
       }
    }
    
    0 讨论(0)
  • 2020-11-21 23:47

    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:

    public sealed class A : IDisposable
    {
        public void Dispose()
        {
            // get rid of managed resources, call Dispose on member variables...
        }
    }
    

    When implementing an unsealed class, do it like this:

    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);
        //}
    }
    

    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 if SuppressFinalize 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:

    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);
        }
    }
    

    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 though B doesn't have a finalizer, it still calls SuppressFinalize 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 a using(...) {} will make sure any unmanaged resources are got rid of as necessary.

    0 讨论(0)
  • 2020-11-21 23:50

    Some aspects of another answer are slightly incorrect for 2 reasons:

    First,

    using(NoGateway objNoGateway = new NoGateway())
    

    actually is equivalent to:

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

    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:

    A finalizer is called upon the GC destroying your object

    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

    0 讨论(0)
提交回复
热议问题