C# 资源回收

在C#中,资源主要分为托管资源(Managed Resources)和非托管资源(Unmanaged Resources)。了解这两种资源的区别对于正确实现IDisposable接口和确保资源得到合理管理是非常重要的。

托管资源(Managed Resources)

托管资源是由.NET运行时直接管理的资源。这些资源通常由.NET框架提供,例如:

  • 字符串(String)
  • 数组(Array)
  • 集合(Collection)
  • 其他.NET类库对象

托管资源的生命周期由.NET垃圾回收器(Garbage Collector,GC)管理。GC会自动回收不再使用的对象,释放它们占用的内存。由于GC的存在,开发者通常不需要手动释放托管资源。

非托管资源(Unmanaged Resources)

非托管资源不是由.NET运行时管理的资源。这些资源需要程序员显式释放,例如:

  • 文件句柄(File Handles)
  • 窗口句柄(Window Handles)
  • 网络连接(Network Connections)
  • 数据库连接(Database Connections)
  • 硬件资源(Hardware Resources)

有个很典型的特征时,托管资源的资源释放流程是很标准的;而非托管资源的释放很可能只有具体的开发者才知道,比如一个网络连接在释放前可能会进行一系列特定的握手和消息通知操作,这写操作GC没法自动完成,必须开发者自己实现IDisposable接口完成,因此非托管资源的生命周期不由GC管理,如果不正确释放,可能会导致资源泄漏或非预期事件发生。

实现IDisposable接口

当你的类使用了一些非托管资源时,应该实现IDisposable接口,以确保这些资源能够被正确释放。IDisposable接口要求实现一个Dispose方法,该方法用于释放资源。通常,Dispose方法会有两个版本:

  • 一个公共的Dispose方法,用于释放托管和非托管资源。
  • 一个受保护的Dispose(bool disposing)方法,用于区分托管资源和非托管资源的释放。

Dispose(bool disposing)方法中:

  • disposingtrue时,释放托管资源。
  • disposingfalse时(通常在析构函数中调用),只释放非托管资源。

示例

public class ResourceHolder : IDisposable
{
    // 用于标记资源是否已经被释放
    private bool _disposed = false;

    // 假设这是一个非托管资源
    private IntPtr _unmanagedResource;

    public void UseResource()
    {
        if (!_disposed)
        {
            // 使用资源
        }
        else
        {
            throw new ObjectDisposedException("ResourceHolder");
        }
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!_disposed)
        {
            if (disposing)
            {
                // 释放托管资源
            }

            // 释放非托管资源
            if (_unmanagedResource != IntPtr.Zero)
            {
                // 假设的释放非托管资源的代码
                // NativeMethods.ReleaseResource(_unmanagedResource);
                _unmanagedResource = IntPtr.Zero;
            }

            _disposed = true;
        }
    }

    ~ResourceHolder()
    {
        // 确保资源被释放
        Dispose(false);
    }
}

// 使用示例
class Program
{
    static void Main()
    {
        using (ResourceHolder resource = new ResourceHolder())
        {
            resource.UseResource();
            // 其他代码...
        }

        // 此时ResourceHolder的Dispose方法会被自动调用
    }
}

ResourceHolder类管理了一个非托管资源(假设为一个指针)。Dispose方法确保了资源被正确释放,而析构函数确保了即使没有显式调用Dispose方法,资源也不会泄漏。

_disposed字段用于跟踪资源是否已经被释放,以防止多次释放资源。

GC.SuppressFinalize(this)调用告诉垃圾回收器不需要为当前对象调用析构函数,因为资源已经被显式释放。

using语句确保了Dispose方法在MyResource对象不再需要时被调用,这是管理资源释放的一种简洁且安全的方式。

posted @ 2024-05-26 14:36  蛮哥哥  阅读(12)  评论(0编辑  收藏  举报