Use of Finalize/Dispose method in C#

后端 未结 13 2075
轮回少年
轮回少年 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:27

    Dispose pattern:

    public abstract class DisposableObject : IDisposable
    {
        public bool Disposed { get; private set;}      
    
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
    
        ~DisposableObject()
        {
            Dispose(false);
        }
    
        private void Dispose(bool disposing)
        {
            if (!Disposed)
            {
                if (disposing)
                {
                    DisposeManagedResources();
                }
    
                DisposeUnmanagedResources();
                Disposed = true;
            }
        }
    
        protected virtual void DisposeManagedResources() { }
        protected virtual void DisposeUnmanagedResources() { }
    }
    

    Example of inheritance:

    public class A : DisposableObject
    {
        public Component components_a { get; set; }
        private IntPtr handle_a;
    
        protected override void DisposeManagedResources()
        {
            try
            {
              Console.WriteLine("A_DisposeManagedResources");
              components_a.Dispose();
              components_a = null;
            }
            finally
            { 
              base.DisposeManagedResources();
            }
        }
    
        protected override void DisposeUnmanagedResources()
        {
            try
            {
              Console.WriteLine("A_DisposeUnmanagedResources");
              CloseHandle(handle_a);
              handle_a = IntPtr.Zero;
            }
            finally
            { 
              base.DisposeUnmanagedResources();
            }
        }
    }
    
    public class B : A
    {
        public Component components_b { get; set; }
        private IntPtr handle_b;
    
        protected override void DisposeManagedResources()
        {
            try
            {
              Console.WriteLine("B_DisposeManagedResources");
              components_b.Dispose();
              components_b = null;
            }
            finally
            { 
              base.DisposeManagedResources();
            }
        }
    
        protected override void DisposeUnmanagedResources()
        {
            try
            {
              Console.WriteLine("B_DisposeUnmanagedResources");
              CloseHandle(handle_b);
              handle_b = IntPtr.Zero;
            }
            finally
            { 
              base.DisposeUnmanagedResources();
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-21 23:28
    using(NoGateway objNoGateway = new NoGateway())
    

    is equivalent to

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

    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.

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

    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:

    #region IDisposable implementation
    
    //TODO remember to make this class inherit from IDisposable -> $className$ : IDisposable
    
    // Default initialization for a bool is 'false'
    private bool IsDisposed { get; set; }
    
    /// <summary>
    /// Implementation of Dispose according to .NET Framework Design Guidelines.
    /// </summary>
    /// <remarks>Do not make this method virtual.
    /// A derived class should not be able to override this method.
    /// </remarks>
    public void Dispose()
    {
        Dispose( true );
    
        // This object will be cleaned up by the Dispose method.
        // Therefore, you should call GC.SupressFinalize to
        // take this object off the finalization queue 
        // and prevent finalization code for this object
        // from executing a second time.
    
        // Always use SuppressFinalize() in case a subclass
        // of this type implements a finalizer.
        GC.SuppressFinalize( this );
    }
    
    /// <summary>
    /// Overloaded Implementation of Dispose.
    /// </summary>
    /// <param name="isDisposing"></param>
    /// <remarks>
    /// <para><list type="bulleted">Dispose(bool isDisposing) executes in two distinct scenarios.
    /// <item>If <paramref name="isDisposing"/> equals true, the method has been called directly
    /// or indirectly by a user's code. Managed and unmanaged resources
    /// can be disposed.</item>
    /// <item>If <paramref name="isDisposing"/> equals false, the method has been called by the 
    /// runtime from inside the finalizer and you should not reference 
    /// other objects. Only unmanaged resources can be disposed.</item></list></para>
    /// </remarks>
    protected virtual void Dispose( bool isDisposing )
    {
        // TODO If you need thread safety, use a lock around these 
        // operations, as well as in your methods that use the resource.
        try
        {
            if( !this.IsDisposed )
            {
                if( isDisposing )
                {
                    // TODO Release all managed resources here
    
                    $end$
                }
    
                // TODO Release all unmanaged resources here
    
    
    
                // TODO explicitly set root references to null to expressly tell the GarbageCollector
                // that the resources have been disposed of and its ok to release the memory allocated for them.
    
    
            }
        }
        finally
        {
            // explicitly call the base class Dispose implementation
            base.Dispose( isDisposing );
    
            this.IsDisposed = true;
        }
    }
    
    //TODO Uncomment this code if this class will contain members which are UNmanaged
    // 
    ///// <summary>Finalizer for $className$</summary>
    ///// <remarks>This finalizer will run only if the Dispose method does not get called.
    ///// It gives your base class the opportunity to finalize.
    ///// DO NOT provide finalizers in types derived from this class.
    ///// All code executed within a Finalizer MUST be thread-safe!</remarks>
    //  ~$className$()
    //  {
    //     Dispose( false );
    //  }
    #endregion IDisposable implementation
    

    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.

    public DerivedClass : BaseClass, IDisposable (remove the IDisposable because it is inherited from BaseClass)
    
    
    protected override void Dispose( bool isDisposing )
    {
        try
        {
            if ( !this.IsDisposed )
            {
                if ( isDisposing )
                {
                    // Release all managed resources here
    
                }
            }
        }
        finally
        {
            // explicitly call the base class Dispose implementation
            base.Dispose( isDisposing );
        }
    }
    

    I've posted this implementation on my blog at: How to Properly Implement the Dispose Pattern

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

    The official pattern to implement IDisposable is hard to understand. I believe this one is better:

    public class BetterDisposableClass : IDisposable {
    
      public void Dispose() {
        CleanUpManagedResources();
        CleanUpNativeResources();
        GC.SuppressFinalize(this);
      }
    
      protected virtual void CleanUpManagedResources() { 
        // ...
      }
      protected virtual void CleanUpNativeResources() {
        // ...
      }
    
      ~BetterDisposableClass() {
        CleanUpNativeResources();
      }
    
    }
    

    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:

    public class NativeDisposable : IDisposable {
    
      public void Dispose() {
        CleanUpNativeResource();
        GC.SuppressFinalize(this);
      }
    
      protected virtual void CleanUpNativeResource() {
        // ...
      }
    
      ~NativeDisposable() {
        CleanUpNativeResource();
      }
    
    }
    

    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:

    public class ManagedDisposable : IDisposable {
    
      public virtual void Dispose() {
        // dispose of managed resources
      }
    
    }
    
    0 讨论(0)
  • 2020-11-21 23:37

    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

    • its obviuos the them from experience (a socket class for example)
    • its documented
    • they are cautious and can see that the class implements IDisposable

    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

    0 讨论(0)
  • 2020-11-21 23:40
    1. 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.

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

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

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