zoukankan      html  css  js  c++  java
  • C#之Dispose

    前言

    谈到Dispose,首先需要理解C#的资源

    资源类型

    • 托管资源:由CLR创建和释放
    • 非托管资源:资源的创建和释放不由CLR管理。比如IO、网络连接、数据库连接等等。需要开发人员手动释放。

    如何释放

    调用的是微软类库或者第三方类库,一般类库会提供释放的方法,即约定为Dispose,调用即可。

    为啥一定是Dispose方法?

    每个类库当然可以提供各自释放资源的方法,比如close()、close()、release()、clear()等等。

    但为了统一,微软提供了IDispose接口,其中只声明了一个void的Dispose()方法。而且还为实现了IDispose接口的类提供了using释放资源的语法糖。

    看代码:

    namespace System
    {
        [ComVisible(true)]
        public interface IDisposable
        {
            //执行与释放或重置非托管资源关联的应用程序定义的任务。
            void Dispose();
        }
    }
    

    忘记释放怎么办?

    比如我们进行一个给图片加水印的功能,使用System.Drwing类库中的Image对象。写代码的时候,我们既不手动调用Dispose方法,也不使用using语法。那么Image对象就一直会留在内存中吗?
    当然不会,Image类有析构函数,在其中调用了Dispose方法。

    上源码:

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

    既然最终析构函数中会释放资源,那么我们是不是没必要手动释放了呢?
    这就要说说析构函数了

    析构函数

    当一个类的实例被GC回收的时候,最终调用的方法。它和构造函数正好相反,后者是在类的实例初始化时调用。

    它的写法是这样子的:

    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类里面不就是这么写的么?
    是呀!

  • 相关阅读:
    Knative Serving 进阶: Knative Serving SDK 开发实践
    从求生存到修体系,我在阿里找到了技术人的成长模式
    K8s 学习者绝对不能错过的最全知识图谱(内含 56个知识点链接)
    P1197 [JSOI2008]星球大战
    P1311 选择客栈
    P2822 组合数问题
    贪心 加工生产调度
    P3375 【模板】KMP字符串匹配
    P1025 数的划分
    P1019 单词接龙
  • 原文地址:https://www.cnblogs.com/talentzemin/p/10986063.html
Copyright © 2011-2022 走看看