本文内容

  • 实现 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;
                }
            }
        }
    }
}

相关文章: