前言
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#托管的,但其中有一些非托管资源,比如网络连接、数据连接、内存。与普通对象相比,只是多了这些资源。
- C#的析构函数(终结器)的作用:
在C#中,析构函数(Finalizer)通常用于释放非托管资源或执行一些清理操作。含有终结期的对象释放具体流程如下:1.当一个对象不再被引用时,垃圾回收器会将其标记为待清理状态。2.如果该对象有终结器(Finalizer),则会被加入到终结队列中等待执行终结器方法。3.终结器方法是一个特殊的析构函数,用于在对象被销毁前执行一些清理操作。4.最后才是内存的真正释放
4.IDisposable接口如何帮助我们释放
IDisposable接口本身中的Disease方法,是用来释放托管资源的,可以主动调用对象的Dispose方法释放。另外c#其实是Using语法糖也支持这一过程,比如:Using(FileStream fs = new FileStream())。这种写法,在超出作用域以后,会自动调用IDisposable接口的Dispose方法。
示例分析
回到最初的两种情况
- 程序员想要立马释放资源
程序员主动调用MyResource的Dispose方法,或者用Using语句。其中Dispose(true)方法真正去释放非托管资源,并告诉GC,不用在释放该对象的资源了。 - 程序员不关心什么时候释放
终结器在不确定的时间,发现可以回收该对象,调用终结器方法,其中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)方法。
- 其参数disposing用来标志是否需要释放其他托管资源(也就是含有资源的托管对象),对象中的disposed是用来标志是否释放过这个对象。
- disposing的if语句中释放的并不是普通的托管对象,比如string,List
,int[]。普通的托管对象,不需要手动释放,GC会进行内存释放。if语句外释放的是本对象的资源。
总结
这种写法很优雅,支持主动、被动,减少内存泄露
标签:IDisposable,释放,c#,托管,Dispose,对象,resources,disposing From: https://www.cnblogs.com/chendasxian/p/18113086