• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

C# 清理非托管资源

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

本文内容

  • 实现 Dispose 方法
  • Finalize 方法和析构函数
  • 使用封装资源的对象
  • 参考

 

通过将对象的范围限制为 protected,以防止直接调用其 Finalize 方法。最好不要直接调用非基类类的 Finalize 方法。

为了适时释放非托管资源,建议实现公共的 DisposeClose 方法。IDisposable 接口为实现接口的资源类提供 Dispose 方法。Dispose 方法是公共的,因此,应用程序用户可以直接调用该方法来释放非托管资源占用的内存。当正确实现 Dispose 方法,可未能调用 Dispose 方法时,Finalize 方法充当防护措施来清理资源。

 

实现 Dispose 方法


实现 Dispose 方法用于释放非托管资源。

释放对象的模式(称为释放模式 dispose pattern)对对象的生存期进行规定。释放方案仅用于非托管资源的对象。因为垃圾回收器对回收未使用的托管对象非常有效。

一个类型的 Dispose 方法应释放它拥有的所有资源,它还应该通过调用其父类型的 Dispose 方法来释放其基类型拥有的所有资源。父类型的 Dispose 方法应该释放它拥有的所有资源,进而调用其父类型的 Dispose 方法,从而在整个基类型层次结构中传播此模式。若要确保资源始终被正确地清理Dispose 方法应该可以多次调用,而不引发异常。

对只使用托管资源的类型(如数组 arrays)实现 Dispose 方法并不能提高性能,因为这些类型由垃圾回收器自动回收。应主要对使用本机资源的托管对象(如 FileStream )和向 .NET Framework 公开的 COM 对象使用 Dispose 方法。

Dispose 方法应该调用 SuppressFinalize 方法来释放对象。如果对象当前在终止队列中,则 SuppressFinalize 会阻止调用其 Finalize 方法。记住,执行 Finalize 方法会降低性能。如果Dispose 方法已完成清理对象的工作,垃圾回收器就不必调用对象的 Finalize 方法。

GC.KeepAlive 方法提供的代码示例演示了,强行垃圾回收如何在回收对象的成员仍在执行时引发终结器(finalizer)运行。在较长的 Dispose 方法最后,最好调用 KeepAlive 方法。

SafeHandle 可选

编写一个对象的终结器代码 finalizer 是一个复杂的任务,如果没有正确完成,可能会导致问题。因此,我们建议构造 SafeHandle 对象,而不是实现释放模式(dispose pattern)。

SafeHandle 类通过分配和不中断的释放句柄,简化对象生存期问题。它包含一个关键的终结器,保证在应用程序域正在卸载时运行。

SafeHandle 类位于 System.Runtime.InteropServices 命名空间,它是一个抽象的包装类,用来操作系统的句柄。从该类派生(继承)是很困难的,但可以使用 Microsoft.Win32.SafeHandles 命名空间下的派生类,它提供以下安全的句柄:

  • 文件和管道
  • 内存视图
  • 加密构造
  • 注册表项
  • 等待句柄
示例

下面演示为封装非托管资源的类实现 Dispose 方法的推荐设计模式。

资源类通常是从复杂的本机类或 API 派生的,而且必须进行相应的自定义。使用这一代码模式作为创建资源类的一个起始点,并根据封装的资源提供必要的自定义。

using System;
using System.IO;
 
namespace AMConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                // 初始化一个 Stream 资源,把它传给 DisposableResource 类
                Console.Write("Enter filename and its path: ");
                string fileSpec = Console.ReadLine();
                FileStream fs = File.OpenRead(fileSpec);
                DisposableResource TestObj = new DisposableResource(fs);
                // 使用资源
                TestObj.DoSomethingWithResource();
                // 释放资源            
                TestObj.Dispose();
                Console.ReadKey();
 
            }
            catch (FileNotFoundException e)
            {
                Console.WriteLine(e.Message);
                Console.ReadKey();
            }
        }
        /// <summary>     
        /// This class shows how to use a disposable resource.   
        /// The resource is first initialized and passed to the constructor, but it could also be initialized in the constructor. 
        /// The lifetime of the resource does not exceed the lifetime of this instance.    
        /// This type does not need a finalizer because it does not directly create a native resource like a file handle or memory in the unmanaged heap.    
        /// </summary>        
        public class DisposableResource : IDisposable
        {
            private Stream _resource;
            private bool _disposed;
            /// <summary>          
            /// 将 stream 传给构造函数,它必须可读、非空     
            /// </summary>        
            /// <param name="stream"></param>    
            public DisposableResource(Stream stream)
            {
                if (stream == null)
                    throw new ArgumentNullException("Stream in null.");
                if (!stream.CanRead)
                    throw new ArgumentException("Stream must be readable.");
                _resource = stream;
                _disposed = false;
            }
            /// <summary>    
            /// 验证资源的使用. 它必须没有被释放     
            /// </summary>            
            public void DoSomethingWithResource()
            {
                if (_disposed)
                    throw new ObjectDisposedException("Resource was disposed.");
                // 显示字节数            
                int numBytes = (int)_resource.Length;
                Console.WriteLine("Number of bytes: {0}", numBytes.ToString());
            }
            public void Dispose()
            {
                Dispose(true);
                // Use SupressFinalize in case a subclass of this type implements a finalizer.        
                GC.SuppressFinalize(this);
            }
            protected virtual void Dispose(bool disposing)
            {
                // 如果需要线程安全,请在这个操作,以及使用资源方法中使用 lock     
                if (!_disposed)
                {
                    if (disposing)
                    {
                        if (_resource != null)
                            _resource.Dispose();
                        Console.WriteLine("Object disposed.");
                    }
                    // Indicate that the instance has been disposed. 
                    _resource = null;
                    _disposed = true;
                }
            }
        }
    }
}

说明

  • 定义 DisposableResource 类,从外部获得文件流,读取其大小,最后释放其文件流的资源。该类继承 IDisposable 接口,并实现 Dispose() 方法;
  • DisposableResource 类的成员函数 DoSomethingWithResource 获得文件流的大小;
  • DisposableResource 类的成员函数 Dispose 极其重载,负责释放资源。
IDisposable 接口

定义一种释放分配的资源的方法。

此接口的主要用途是释放非托管资源。当不再使用托管对象时,垃圾回收器会自动释放分配给该对象的内存。但无法预测进行垃圾回收的时间。另外,垃圾回收器对窗口句柄或打开的文件和流等非托管资源一无所知。

将此接口的 Dispose 方法与垃圾回收器一起使用来显式释放非托管资源。当不再需要对象时,对象的使用者可以调用此方法。

向现有类添加 IDisposable 接口是重大的更改,因为这更改了类的语义。

有关如何使用此接口和 Object.Finalize 方法的详细讨论。

在调用可实现 IDisposable 接口的类时,请使用 try/finally 模式来确保非托管资源能够释放出来,即使应用程序因出现异常而中断也是如此。

有关 try/finally 模式的更多信息,请参见try-finally(C# 参考)或 Thetry-finally Statement。

请注意,您可以使用 using 语句(在 Visual Basic 中为 Using)来代替 try/finally 模式。有关更多信息,请参见 Using 语句 (Visual Basic) 文档或 using 语句(C# 参考)文档。

示例

下面演示如何创建用来实现 IDisposable 接口的资源类。

using System;
using System.ComponentModel;
public class DisposeExample
{
    /// <summary>
    /// A base class that implements IDisposable. 
    /// By implementing IDisposable, you are announcing that  instances of this type allocate scarce resources.   
    /// </summary>
    public class MyResource : IDisposable
    {
        // 指向外部非托管资源(external unmanaged resource)    
        private IntPtr handle;
        // 类中使用其他托管资源      
        private Component component = new Component();
        // 追踪 Dispose 是否被调用    
        private bool disposed = false;
 
        public MyResource(IntPtr handle)
        {
            this.handle = handle;
        }
        /// <summary>
        /// 实现 IDisposable    
        /// </summary>
        /// <remarks>
        /// 不要将该方法设置为 virtual,这样,它的子类就不会 override 这个方法       
        /// </remarks>
        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);
        }
        /// <summary>
        /// 在两个常见的情况下执行 Dispose(bool disposing)   
        /// </summary>
        /// <remarks>
        /// 如果 disposing 为 true,那么这个方法直接或间接地被用户代码调用,托管和非托管资源会被释放。
        /// 如果 disposing 为 false,那么这个方法会在终结器(finalizer)内被运行时调用,你不能引用其他对象。
        /// 只有非托管资源才可以被释放。  
        /// </remarks>
        /// <param name="disposing"></param>
        private void Dispose(bool disposing)
        {
            // 检查 Dispose 方法是否被调用    
            if (!this.disposed)
            {
                // 如果 disposing 为 true,那么释放所有的托管和非托管资源     
                if (disposing)
                {
                    // 释放托管资源   
                    component.Dispose();
                }
                // 调用适当的方法,清除非托管资源             
                // 如果 disposing 为 false,那么仅执行下面代码    
                CloseHandle(handle);
                handle = IntPtr.Zero;
                // 释放完成       
                disposed = true;
            }
        }
        /// <summary>
        /// 使用 interop 来调用需要清除非托管资源的方法
        /// </summary>
        /// <param name="handle"></param>
        /// <returns></returns>
        [System.Runtime.InteropServices.DllImport("Kernel32")]
        private extern static Boolean CloseHandle(IntPtr handle);
        /// <summary>
        /// Use C# destructor syntax for finalization code.  

 
                    
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
c#通过OleDb连接sybase 15.5发布时间:2022-07-13
下一篇:
c#获取图像像素发布时间:2022-07-13
热门推荐
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap