首页 > 编程语言 >c#非托管资源释放

c#非托管资源释放

时间:2024-04-03 21:24:42浏览次数:22  
标签:IDisposable 释放 c# 托管 Dispose 对象 resources disposing

前言

 c#一般使用托管内存,不用担心资源释放问题。但如果调用操作系统资源,比如文件、窗口的句柄,数据库及网络连接;或者PInvoke调用C++的库。此时,需要程序员手动对这些资源进行管理。其中IDisposable接口,终结器,可以帮助我们封装这一释放过程。

官方示例

   微软的官方文档如下。这个类看起来有点繁琐,主要是为了支持多种情况:1.程序员想要立马释放资源2.程序员不关心什么时候释放,只要最终释放了就行。

using System;
using System.ComponentModel;

// The following example demonstrates how to create
// a resource class that implements the IDisposable interface
// and the IDisposable.Dispose method.

public class DisposeExample
{
    // A base class that implements IDisposable.
    // By implementing IDisposable, you are announcing that
    // instances of this type allocate scarce resources.
    public class MyResource: IDisposable
    {
        // Pointer to an external unmanaged resource.
        private IntPtr handle;
        // Other managed resource this class uses.
        private Component component = new Component();
        // Track whether Dispose has been called.
        private bool disposed = false;

        // The class constructor.
        public MyResource(IntPtr handle)
        {
            this.handle = handle;
        }

        // Implement IDisposable.
        // Do not make this method virtual.
        // A derived class should not be able to override this method.
        public void Dispose()
        {
            Dispose(disposing: true);
            // This object will be cleaned up by the Dispose method.
            // Therefore, you should call GC.SuppressFinalize to
            // take this object off the finalization queue
            // and prevent finalization code for this object
            // from executing a second time.
            GC.SuppressFinalize(this);
        }

        // Dispose(bool disposing) executes in two distinct scenarios.
        // If disposing equals true, the method has been called directly
        // or indirectly by a user's code. Managed and unmanaged resources
        // can be disposed.
        // If disposing 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.
        protected virtual void Dispose(bool disposing)
        {
            // Check to see if Dispose has already been called.
            if(!this.disposed)
            {
                // If disposing equals true, dispose all managed
                // and unmanaged resources.
                if(disposing)
                {
                    // Dispose managed resources.
                    component.Dispose();
                }

                // Call the appropriate methods to clean up
                // unmanaged resources here.
                // If disposing is false,
                // only the following code is executed.
                CloseHandle(handle);
                handle = IntPtr.Zero;

                // Note disposing has been done.
                disposed = true;
            }
        }

        // Use interop to call the method necessary
        // to clean up the unmanaged resource.
        [System.Runtime.InteropServices.DllImport("Kernel32")]
        private extern static Boolean CloseHandle(IntPtr handle);

        // Use C# finalizer syntax for finalization code.
        // 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 finalizer in types derived from this class.
        ~MyResource()
        {
            // Do not re-create Dispose clean-up code here.
            // Calling Dispose(disposing: false) is optimal in terms of
            // readability and maintainability.
            Dispose(disposing: false);
        }
    }
    public static void Main()
    {
        // Insert code here to create
        // and use the MyResource object.
    }
}

相关概念

1.普通C#对象的释放:
 普通的C#对象在不再被引用时,会由垃圾回收器自动进行内存释放。当对象不再被引用时,垃圾回收器会在适当的时机将其标记为可回收对象,并在之后的垃圾回收过程中释放其占用的内存空间。

2.什么是含有资源的托管对象:
 也就是该对象本身是c#托管的,但其中有一些非托管资源,比如网络连接、数据连接、内存。与普通对象相比,只是多了这些资源。

  1. C#的析构函数(终结器)的作用:
     在C#中,析构函数(Finalizer)通常用于释放非托管资源或执行一些清理操作。含有终结期的对象释放具体流程如下:1.当一个对象不再被引用时,垃圾回收器会将其标记为待清理状态。2.如果该对象有终结器(Finalizer),则会被加入到终结队列中等待执行终结器方法。3.终结器方法是一个特殊的析构函数,用于在对象被销毁前执行一些清理操作。4.最后才是内存的真正释放

4.IDisposable接口如何帮助我们释放
 IDisposable接口本身中的Disease方法,是用来释放托管资源的,可以主动调用对象的Dispose方法释放。另外c#其实是Using语法糖也支持这一过程,比如:Using(FileStream fs = new FileStream())。这种写法,在超出作用域以后,会自动调用IDisposable接口的Dispose方法。

示例分析

 回到最初的两种情况

  1. 程序员想要立马释放资源
     程序员主动调用MyResource的Dispose方法,或者用Using语句。其中Dispose(true)方法真正去释放非托管资源,并告诉GC,不用在释放该对象的资源了。
  2. 程序员不关心什么时候释放
     终结器在不确定的时间,发现可以回收该对象,调用终结器方法,其中Dispose(false)去真正释放资源。
protected virtual void Dispose(bool disposing)
        {
            // Check to see if Dispose has already been called.
            if(!this.disposed)
            {
                // If disposing equals true, dispose all managed
                // and unmanaged resources.
                if(disposing)
                {
                    // Dispose managed resources.
                    component.Dispose();
                }

                // Call the appropriate methods to clean up
                // unmanaged resources here.
                // If disposing is false,
                // only the following code is executed.
                CloseHandle(handle);
                handle = IntPtr.Zero;

                // Note disposing has been done.
                disposed = true;
            }
        }

最后是分析Dispose(bool disposing)方法。

  1. 其参数disposing用来标志是否需要释放其他托管资源(也就是含有资源的托管对象),对象中的disposed是用来标志是否释放过这个对象。
  2. disposing的if语句中释放的并不是普通的托管对象,比如string,List,int[]。普通的托管对象,不需要手动释放,GC会进行内存释放。if语句外释放的是本对象的资源。

总结

 这种写法很优雅,支持主动、被动,减少内存泄露

标签:IDisposable,释放,c#,托管,Dispose,对象,resources,disposing
From: https://www.cnblogs.com/chendasxian/p/18113086

相关文章

  • 盘点一个dbeaver导入csv文件到sql server报错的一个问题
    大家好,我是Python进阶者。一、前言前几天在Python最强王者交流群【金光灿灿】问了一个dbeaver导入csv文件到sqlserver报错的一个问题,问题如下:我在使用dbeaver导入csv文件到sqlserver时一直出现Can'tparsenumericvalue[B02010ZZZ]usingformatter这样的报错二、实现过程......
  • Cush:从辞职自学编程到被 Apple、PriceTag推荐
    名字:Cush开发者/团队:ShaSha平台:iOS、macOS请简要介绍下这款产品也许你听过记账可以帮助省钱,但总是浅尝辄止?快试试Cush!它精简了记账中所有复杂繁琐的步骤,简单精美,让你轻松养成记账习惯!与此同时,在你超支时,Cush还会醒目地提醒你,让你每一天、每一笔都能省钱!这个月末不再超......
  • IDEA 中的代码生成器(CodeGenerator)的使用
    代码生成器的使用在IDEA中,为了方便简化代码编写,可以引入代码生成器CodeGenerator类。这个类可以根据数据库中存在的表,自动在IDEA中生成Controller类、Entity类、Mapper类、Sevice类、ServiceImpl扩展类、以及xml文件。使用方法:在项目目录下新建一个common包,直接ctrl......
  • dfs 序求 LCA!
    前言为什么用dfs序求LCA而不用欧拉序?帅常数小,也就一半好玩反正没什么正经理由。正文定义dfs序是指对树进行深度优先遍历后得到的节点序列。\(\mathit{dfn}_i\)是节点\(i\)在dfs序中的位置(从\(0\)或\(1\)开始无影响)。LCA是最近公共祖先。深度\(\ma......
  • ctfshow--web4
    这题和第三题有点不一样,这题的把php和data都过滤掉了一旦我们输入这个关键字就页面就会报error一开始是没啥头绪的,后面上网查了一下,可以通过日志记录来注入代码对于Apache,日志存放路径:/var/log/apache/access.log对于Ngnix,日志存放路径:/var/log/nginx/access.log和/var/......
  • 洛谷:P8671 [蓝桥杯 2018 国 AC] 约瑟夫环
    时间限制1.00s     内存限制256.00MB     难易度:普及+/提高【题目描述】n 个人的编号是1∼n,如果他们依编号按顺时针排成一个圆圈,从编号是 1 的人开始顺时针报数。(报数是从 1 报起)当报到 k 的时候,这个人就退出游戏圈。下一个人重新从 1 开始报......
  • C++ While 和 For 循环:流程控制全解析
    C++Switch语句使用switch语句选择要执行的多个代码块之一。语法switch(expression){casex://代码块break;casey://代码块break;default://代码块}它的工作原理如下:switch表达式被评估一次表达式的值与每个case的值进行比......
  • 前端学习思维导图总结~~~CSS篇
    一、前端学习总结CSS部分:二、随记分享这是前端学习过程中总结的思维导图,总结并分享出来,希望给有需要的朋友呀一些帮助,给各位看官一些参考总结的思维导图文件在 主页资源(免费):前端三件套之一:css学习总结思维导图资源-CSDN文库https://download.csdn.net/download/m0_615......
  • dfs 序求 LCA!
    前言为什么用dfs序求LCA而不用欧拉序?帅常数小,也就一半好玩反正没什么正经理由。正文定义dfs序是指对树进行深度优先遍历后得到的节点序列。\(\mathit{dfn}_i\)是节点\(i\)在dfs序中的位置(从\(0\)或\(1\)开始无影响)。LCA是最近公共祖先。深度\(\ma......
  • C#获取文本的编码格式
    这段代码首先定义了一个方法DetectFileEncoding,它打开指定路径的文件,读取其前4096个字节(这个数量可以根据需要调整)。接着,它遍历一个包含常见编码(如UTF-8、Unicode、ASCII等)的列表,并尝试使用每个编码将读取到的字节序列解码为字符串。请注意,此方法并非完全可靠,因为某些编码可能存......