原文:https://www.cnblogs.com/luminji/archive/2011/03/29/1997812.html

IDisposable高级篇:https://docs.microsoft.com/zh-cn/dotnet/standard/garbage-collection/implementing-dispose

需要明确一下C#程序(或者说.NET)中的资源。简单的说来,C#中的每一个类型都代表一种资源,而资源又分为两类:

托管资源:由CLR管理分配和释放的资源,即由CLR里new出来的对象;

非托管资源:不受CLR管理的对象,windows内核对象,如文件、数据库连接、套接字、COM对象、stream 流等;IDisposable仅当类型直接使用非托管资源时,才应实现

毫无例外地,如果我们的类型使用到了非托管资源,或者需要显式释放的托管资源,那么,就需要让类型继承接口IDisposable。这相当于是告诉调用者,该类型是需要显式释放资源的,你需要调用我的Dispose方法。IDisposable接口:定义一种释放分配的资源的方法。

一、IDisposable的接口定义如下

public interface IDisposable
{
// Summary:
// Performs application-defined tasks associated with freeing, releasing, or
// resetting unmanaged resources.
void Dispose();
}

using 语句定义一个范围,在此范围的末尾将释放非托管资源对象。

二:IDisposable基本应用

1.下面的示例演示如何创建实现接口的资源类 IDisposable

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(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.
GC.SuppressFinalize(this);//通知垃圾回收器不再调用终结器
}
//Dispose 方法执行所有对象清理,使垃圾回收器不再需要调用对象的 Object.Finalize 重写。 因此,调用 SuppressFinalize 方法会阻止垃圾回收器运行终结器。 如果类型没有终结器,则对 GC.SuppressFinalize 的调用不起作用。 请注意,实际清除由 Dispose(bool) 方法重载执行。 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# destructor syntax for finalization code.
// This destructor will run only if the Dispose method
// does not get called.
// It gives your base class the opportunity to finalize.
// Do not provide destructors in types derived from this class.
//~MyResource()是析构函数,这边是假设使用非托管代码时候忘记释放资源,才会用到析构函数释放资源。析构函数本身就是负释非托管放资源的,因此不用再使用Dispose()函数再释放一次资源,因此给传给Dispose false参数。

~MyResource()
{
// Do not re-create Dispose clean-up code here.
// Calling Dispose(false) is optimal in terms of
// readability and maintainability.
Dispose(false);//用到终结器 就是说以忘记了调用Dispose(true)释放资源了。Dispose(false)= Dispose(忘记)


}
}
public static void Main()
{
// Insert code here to create
// and use the MyResource object.
}
}

2、两种方式来调用

第一种:使用using 语句,则可以使用它,而无需显式调用 IDisposable.Dispose 自己。
using语句实际上是语法糖。 在编译时,语言编译器将为块实现中间语言 (IL) try / finally 。

using System;
using System.IO;
using System.Text.RegularExpressions; public class WordCount
{
private String filename = String.Empty;
private int nWords = 0;
private String pattern = @"\b\w+\b"; public WordCount(string filename)
{
if (! File.Exists(filename))
throw new FileNotFoundException("The file does not exist."); this.filename = filename;
string txt = String.Empty;
using (StreamReader sr = new StreamReader(filename)) {
txt = sr.ReadToEnd();
}

nWords = Regex.Matches(txt, pattern).Count;
} public string FullName
{ get { return filename; } } public string Name
{ get { return Path.GetFileName(filename); } } public int Count
{ get { return nWords; } }
}
//C#8.0以后 using var StreamReader sr = new StreamReader(filename);//表示在函数范围结束前调用Dispose()释放非托管资源
static int WriteLinesToFile(IEnumerable<string> lines)
{
using var file = new System.IO.StreamWriter("WriteLines2.txt");
int skippedLines = 0;
foreach (string line in lines)
{
if (!line.Contains("Second"))
{
file.WriteLine(line);
}
else
{
skippedLines++;
}
}
// Notice how skippedLines is in scope here.
return skippedLines;
// file is disposed here
}

第二种: try / finally显示的释放资源

using System;
using System.IO;
using System.Text.RegularExpressions; public class WordCount
{
private String filename = String.Empty;
private int nWords = 0;
private String pattern = @"\b\w+\b"; public WordCount(string filename)
{
if (! File.Exists(filename))
throw new FileNotFoundException("The file does not exist."); this.filename = filename;
string txt = String.Empty;
StreamReader sr = null;
try {
sr = new StreamReader(filename);
txt = sr.ReadToEnd();
}
finally {
if (sr != null) sr.Dispose();
}
nWords = Regex.Matches(txt, pattern).Count;
} public string FullName
{ get { return filename; } } public string Name
{ get { return Path.GetFileName(filename); } } public int Count
{ get { return nWords; } }
}

出现无法无法调用的情况 Dispose

若要处理其无法调用的情况 Dispose ,您应该使用从派生的类 SafeHandle 来包装非托管资源,或者您应该 Object.Finalize  (析构函数)为引用类型重写方法。 在任一情况下,使用方法可以在 Dispose 使用非托管资源(例如释放、释放或重置非托管资源)后执行任何需要的清理。

IDisposable 和继承层次结构

//注意:基类型不应包括任何Finalize终结器 (析构函数)

具有应可释放的子类的基类必须 IDisposable 按如下方式实现。 在 IDisposable sealed Visual Basic) 中未 (的任何类型上实现时,应该使用此模式 NotInheritable

  • 它应提供一个公共、非虚拟 Dispose() 方法和受保护的虚拟 Dispose(Boolean disposing) 方法。

  • Dispose()方法必须调用 Dispose(true) ,并且应禁止终止以提高性能。

  • 基类型不应包括任何终结器。

下面的代码段反映了基类的释放模式。 它假定您的类型不重写 Object.Finalize 方法。

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

子类应实现以下可释放模式:

  • 它们必须重写 Dispose(Boolean) 并调用基类 Dispose(Boolean) 实现。

  • 如果需要,他们可以提供终结器。 终结器必须调用 Dispose(false)

请注意,派生类本身并不实现 IDisposable 接口,并且不包含无参数 Dispose 方法。 它们仅重写基类 Dispose(Boolean) 方法。

下面的代码段反映派生类的释放模式。 它假定您的类型不重写 Object.Finalize 方法。

using Microsoft.Win32.SafeHandles;
using System;
using System.Runtime.InteropServices; class DerivedClass : BaseClass
{
// Flag: Has Dispose already been called?
bool disposed = false;
// Instantiate a SafeHandle instance.
SafeHandle handle = new SafeFileHandle(IntPtr.Zero, true); // Protected implementation of Dispose pattern.
protected override void Dispose(bool disposing)
{
if (disposed)
return; if (disposing) {
handle.Dispose();
// Free any other managed objects here.
//
} // Free any unmanaged objects here.
// disposed = true;
// Call base class implementation.
base.Dispose(disposing);
}
}

IDisposable?释放非托管资源接口的更多相关文章

  1. 5.C#释放非托管资源1

    释放非托管资源 在介绍释放非托管资源的时候,我觉得有必要先来认识一下啥叫非托管资源,既然有非托管资源,肯定有托管资源. 托管资源指的是.net可以自棕进行回收的资源,主要是指托管堆上分配的内存资源.托 ...

  2. C#编程(七十四)----------释放非托管资源

    释放非托管资源 在介绍释放非托管资源的时候,我觉得有必要先来认识一下啥叫非托管资源,既然有非托管资源,肯定有托管资源. 托管资源指的是.net可以自棕进行回收的资源,主要是指托管堆上分配的内存资源.托 ...

  3. [转]在C#中使用托管资源和非托管资源的区别,以及怎样手动释放非托管资源:

    托管资源指的是.NET可以自动进行回收的资源,主要是指托管堆上分配的内存资源.托管资源的回收工作是不需要人工干预的,有.NET运行库在合适调用垃圾回收器进行回收. 非托管资源指的是.NET不知道如何回 ...

  4. C# 释放非托管资源

    C#中资源分为托管资源和非托管资源. 托管资源由垃圾回收器控制如何释放,不需要程序员过多的考虑(当然也程序员也可以自己释放). 非托管资源需要自己编写代码来释放.那么编写好的释放非托管资源的代码(释非 ...

  5. 6.C# 释放非托管资源2

    C# 释放非托管资源 C#中资源分为托管资源和非托管资源. 托管资源由垃圾回收器控制如何释放,不需要程序员过多的考虑(当然也程序员也可以自己释放). 非托管资源需要自己编写代码来释放.那么编写好的释放 ...

  6. Dispose模式释放非托管资源

    实现方式用的是设计模式里的模板模式,基类先搭好框架,子类重写void Dispose(bool disposing) 即可. 需要注意的是基类的Finalize函数也就是析构函数调用的是虚函数void ...

  7. 利用IDisposable接口构建包含非托管资源对象

    托管资源与非托管资源 在.net中,对象使用的资源分为两种:托管资源与非托管资源.托管资源由CLR进行管理,不需要开发人员去人工进行控制,.NET中托管资源主要指"对象在堆中的内存" ...

  8. SQL 横转竖 、竖专横 (转载) 使用Dapper.Contrib 开发.net core程序,兼容多种数据库 C# 读取PDF多级书签 Json.net日期格式化设置 ASPNET 下载共享文件 ASPNET 文件批量下载 递归,循环,尾递归 利用IDisposable接口构建包含非托管资源对象 《.NET 进阶指南》读书笔记2------定义不可改变类型

    SQL 横转竖 .竖专横 (转载)   普通行列转换 问题:假设有张学生成绩表(tb)如下: 姓名 课程 分数 张三 语文 74 张三 数学 83 张三 物理 93 李四 语文 74 李四 数学 84 ...

  9. .net 资源释放(托管资源和非托管资源)

    1.托管资源 像int.float.DateTime等都是托管资源:net中80%的资源都是托管资源: 托管资源的回收通过GC(垃圾回收器)自动释放分配给该对象的内存,但无法预测进行垃圾回收的时间,我 ...

随机推荐

  1. thrift基础知识

    1. 架构图 Thrift 包含一个完整的堆栈结构用于构建客户端和服务器端.下图描绘了 Thrift 的整体架构. 图 1. 架构图 如图所示,图中黄色部分是用户实现的业务逻辑,褐色部分是根据 Thr ...

  2. zabbix安装 报错 socket '/var/lib/mysql/mysql.sock' (13)]

    安装界面提示: Error connecting to database: Can't connect to local MySQL server through socket '/var/lib/m ...

  3. (1)puppet安装

    简介: 基于C/S架构的Puppet更新方式一般有两种,一种是Agent端设置同步时间主动去PuppetMaster端拉取配置,另一种是通过PuppetMaster端使用puppet kick命令或者 ...

  4. IDEA出现Cannot resolve symbol “xxx“(无法解析符号)的解决办法

    1,File->Invalidate Caches/Restart 清除缓存并重启 idea 2,检查pom文件中的依赖关系是否正确 3,maven -> Reimport 4,打开pro ...

  5. jenkins自动化pipline的ci/cd流水线

    pipeline { agent any tools { //工具必须预先在jenkins中预配置 maven 'mvn' jdk 'jdk' } stages { stage('Env') { st ...

  6. cobbler最小化安装centos8

    centos8 已经发布了GA版本,迫不及待的想尝鲜了,然后现实总是那么残酷,一直安装失败,具体安装步骤如下: 假设cobbler已配置完成. 1.下载centos8 iso镜像 wget http: ...

  7. python网络爬虫-入门(一)

    前言 1.爬虫程序是Dt(Data Technology,数据技术)收集信息的基础,爬取到目标网站的资料后,就可以分析和建立应用了. 2.python是一个简单.有效的语言,爬虫所需要的获取.存储.整 ...

  8. Java架构系列问题合集-目录

    接下来会做一个系列, 分类说明关于Java项目研发和架构工作需要了解的问题 Java语法 Java语法专题1: 类初始化的构造顺序 https://www.cnblogs.com/milton/p/1 ...

  9. Homework_3 (完整版)

    划水‍♂️!好耶! 果然还是逃不过作业,初三刚过就要营业 审题 爬虫+算法:划水中的员工 员工 A 此刻内心一酸,大年初一加班惨绝人寰,情不自禁打开 B 站,跟着网友一起划水看番. 但是由于技术故障原 ...

  10. Java编程中标识符注意点以及注释

    标识符注意点 所有的标识符都应该以字母(A-Z或者a-z),美元符($),或者下划线(_)开始 首字符之后可以是字母(A-Z或者a-z),美元符($),下划线(_)或数 字的任何字符组合 不能使用关键 ...