IDisposable?释放非托管资源接口
原文: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); } }