C#资源回收和IDisposable接口的使用

源码地址:https://github.com/hiramtan/HiFramework_unity/blob/master/unity/Assets/HiFramework/Extensions/ObjectBase.csgit

在说资源回收以前先要说明托管资源和非托管资源。github

1.托管资源由CLR来维护,自动进行垃圾回收,好比数组。数组

2.非托管资源不会进行自动垃圾回收,须要手动释放,好比句柄。但在C#中的非托管资源不少都被封装到.NET类中,当对象释放时内部方法同时释放非托管资源。缓存

好比Socket链接,在.Net中被封装为Socket类,反编译Socket类库,看到建立链接对象时其实建立了一个句柄安全

可是当上层用户使用socket的时候并无发现有过释放句柄的逻辑,这部分释放逻辑由谁完成的?咱们继续反编译Socket的Close方法。socket

最终将会调用到本篇所要涉及的IDisposable接口。在Disposable中Socket内部逻辑释放了托管资源好比缓存和非托管资源,好比链接句柄。函数

既然说清楚了托管资源和非托管资源,下面正式说下IDisposable接口的使用。this

在C#中建立的对象,数组,列表...等等都不须要考虑资源释放的问题,由于它会被CLR的垃圾回收机制自动回收。好比有个ObjectBase类,建立这个对象而后置空,它会一段时间后被自动回收:对象

ObjectBase ob = new ObjectBase();blog

ob = null;

那怎样肯定这个对象确实是被正常回收了呢,咱们引入它的析构函数:

public class ObjectBase

{

~ObjectBase()

{

Console.WriteLine("Dispose finish");

}

}

如今出现一个问题:建立的对象释放时是随机的不肯定时长的等待自动回收机制进行收回,有没有办法主动回收这些资源呢,好比对象占用内存较大,想主动当即释放而不等待主动回收。能够写一个方法,在这个方法里释放引用的资源就能够了,这个方法能够随便起名,固然更规范的是继承IDisposable来实现Disposable方法。

好比在这个方法里释放申请的托管资源和非托管资源等,如今ObjectBase类变成以下:

public class ObjectBase:IDisposable

{

~ObjectBase()

{

Console.WriteLine("Dispose finish");

}

 

/// <summary>执行与释放或重置非托管资源关联的应用程序定义的任务。</summary>

public void Dispose()

{

}

}

如今又出现一个问题,有些用户会调用ob.Dispose()同时释放了托管资源和非托管资源,有些用户只是ob = null什么都没有作等待系统的垃圾回收。能不能有更统一的方法?固然能够,咱们把释放资源的逻辑不写在Dispose()方法中,而是写在另外一个方法中,好比Dispose(bool disposing)中,让用户主动调用的,或者析构函数都调用这个方法就同时作到了释放资源。这样还有一个问题,当用户主动调用Dispose()时,其实Dispose(bool disposing)被执行了两次(主动调用一次,析构一次)咱们添加一个标识,标记他只能被标记一次防止屡次调用。

public class ObjectBase : IDisposable

{

private bool disposed = false;

~ObjectBase()

{

Dispose(false);

}

/// <summary>执行与释放或重置非托管资源关联的应用程序定义的任务。</summary>

public void Dispose()

{

Dispose(true);

}

private void Dispose(bool disposing)

{

if (!disposed)

{

 

}

disposed = true;

}

}

再继续,Dispose(bool disposing)为何传入bool类型的变量?其实这个变量标记的是是否可以安全释放,托管资源由垃圾回收机制回收,当析构函数执行时它所引用的对象说不定早就被回收,好比再去取这个List,而后执行Clear时确定会报错,由于List早已被回收。当用户调用Dispose接口时,这个对象还未触发垃圾回收,能够随意拿来Clear,因此逻辑又会变成:

public class ObjectBase : IDisposable

{

private bool disposed = false;

~ObjectBase()

{

Dispose(false);

}

/// <summary>执行与释放或重置非托管资源关联的应用程序定义的任务。</summary>

public void Dispose()

{

Dispose(true);

}

private void Dispose(bool disposing)

{

if (!disposed)

{

if (disposing)

{

//释放托管资源

}

//释放非托管资源

}

disposed = true;

}

}

这样也会存在一个问题:当用户主动调用Dispose时释放非托管的逻辑执行了两次(一次主动调用,一次析构)那咱们来通知系统不要执行回收该对象来避免两次执行,至此释放接口的逻辑完成:

public class ObjectBase : IDisposable

{

private bool disposed = false;

~ObjectBase()

{

Dispose(false);

}

/// <summary>执行与释放或重置非托管资源关联的应用程序定义的任务。</summary>

public void Dispose()

{

Dispose(true);

GC.SuppressFinalize(this);

}

private void Dispose(bool disposing)

{

if (!disposed)

{

if (disposing)

{

//释放托管资源

}

//释放非托管资源

}

disposed = true;

}

}

可是用户使用起来太不方便了,咱们封装一下,能够继承该类方便的使用。由于释放托管资源在C#里面操做很频繁,非托管资源大部分被封装只是不多的状况下使用,咱们分别把接口封装为强制重写和可重写。

public abstract class ObjectBase : IDisposable

{

private bool disposed = false;

~ObjectBase()

{

Dispose(false);

}

/// <summary>执行与释放或重置非托管资源关联的应用程序定义的任务。</summary>

public void Dispose()

{

Dispose(true);

GC.SuppressFinalize(this);

}

private void Dispose(bool disposing)

{

if (!disposed)

{

if (disposing)

{

DisposeManaged();

}

DisposeUnmanaged();

}

disposed = true;

}

 

protected abstract void DisposeManaged();//释放托管资源

 

protected virtual void DisposeUnmanaged()//释放非托管资源

{

 

}

}

相关文章
相关标签/搜索