C#之Dispose

前言

談到Dispose,首先須要理解C#的資源數據庫

資源類型

  • 託管資源:由CLR建立和釋放
  • 非託管資源:資源的建立和釋放不禁CLR管理。好比IO、網絡鏈接、數據庫鏈接等等。須要開發人員手動釋放。

如何釋放

調用的是微軟類庫或者第三方類庫,通常類庫會提供釋放的方法,即約定爲Dispose,調用便可。api

爲啥必定是Dispose方法?

每一個類庫固然能夠提供各自釋放資源的方法,好比close()、close()、release()、clear()等等。網絡

但爲了統一,微軟提供了IDispose接口,其中只聲明瞭一個void的Dispose()方法。並且還爲實現了IDispose接口的類提供了using釋放資源的語法糖。ide

看代碼:函數

namespace System
{
    [ComVisible(true)]
    public interface IDisposable
    {
        //執行與釋放或重置非託管資源關聯的應用程序定義的任務。
        void Dispose();
    }
}

忘記釋放怎麼辦?

好比咱們進行一個給圖片加水印的功能,使用System.Drwing類庫中的Image對象。寫代碼的時候,咱們既不手動調用Dispose方法,也不使用using語法。那麼Image對象就一直會留在內存中嗎?
固然不會,Image類有析構函數,在其中調用了Dispose方法。性能

上源碼:this

public void Dispose()
{
    this.Dispose(true);
    GC.SuppressFinalize(this);
}

protected virtual void Dispose(bool disposing)
{
    if (this.nativeImage != IntPtr.Zero)
    {
        try
        {
            SafeNativeMethods.Gdip.GdipDisposeImage(new HandleRef(this, this.nativeImage));
        }
        catch (Exception ex)
        {
            if (ClientUtils.IsSecurityOrCriticalException(ex))
            {
                throw;
            }
        }
        finally
        {
            this.nativeImage = IntPtr.Zero;
        }
    }
}

~Image()
{
    this.Dispose(false);
}

既然最終析構函數中會釋放資源,那麼咱們是否是不必手動釋放了呢?
這就要說說析構函數了spa

析構函數

當一個類的實例被GC回收的時候,最終調用的方法。它和構造函數正好相反,後者是在類的實例初始化時調用。線程

它的寫法是這樣子的:code

class Car
{
    ~Car()  // finalizer
    {
        // cleanup statements...
    }
}

隱式的調用了基類的Finalize方法,因此等價下面的寫法:

protected override void Finalize()  
{  
    try  
    {  
        // Cleanup statements...  
    }  
    finally  
    {  
        base.Finalize();  
    }  
}

Finalize操做呢,有如下限制:

  • The exact time when the finalizer executes is undefined.
  • The finalizers of two objects are not guaranteed to run in any specific order, even if one object refers to the other.
  • The thread on which the finalizer runs is unspecified.

(來源:https://docs.microsoft.com/en-us/dotnet/api/system.object.finalize?view=netframework-4.8 )

簡單理解就是Finalize操做由GC決定,回收的時間不定、順序不定、線程不定。因此析構函數中調用Dispose只是一個保險措施,並不能代替手動釋放資源。

好比數據庫鏈接,你打開鏈接不及時釋放,很快就沒法鏈接新的數據庫了。而此時GC有可能還未執行析構函數。

固然,析構函數在GC回收的時候,還會由於垃圾回收機制有其餘性能問題,詳細咱們在垃圾回收機制的文章中講。

(參考:https://www.viva64.com/en/b/0437/

Dispose模式

因此,到目前爲止,咱們清楚的知道,對於非託管資源的使用,必定要記得釋放資源。

咱們給被人提供類庫的時候,也明白了到底何時須要實現IDispose接口了。

固然,Dispose的實現已然有套路了,稱之爲Dispose模式,如下是示例:

using Microsoft.Win32.SafeHandles;
using System;
using System.Runtime.InteropServices;

class BaseClass : IDisposable
{
   // Flag: Has Dispose already been called?
   bool disposed = false;
   // Instantiate a SafeHandle instance.
   SafeHandle handle = new SafeFileHandle(IntPtr.Zero, true);
   
   // Public implementation of Dispose pattern callable by consumers.
   public void Dispose()
   { 
      Dispose(true);
      GC.SuppressFinalize(this);           
   }
   
   // Protected implementation of Dispose pattern.
   protected virtual void Dispose(bool disposing)
   {
      if (disposed)
         return; 
      
      if (disposing) {
         handle.Dispose();
         // Free any other managed objects here.
         //
      }
      
      disposed = true;
   }
}

喲,剛纔上面的Image類裏面不就是這麼寫的麼? 是呀!

相關文章
相關標籤/搜索