`

.NET中IDisposable接口的基本使用

阅读更多

首先来看MSDN中关于这个接口的说明:

[ComVisible(true)]
public interface IDisposable
{
    // Methods
    void Dispose();
}
1.[ComVisible(true)]:指示该托管类型对 COM 是可见的.

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

一:基本应用

1.我们来定义一个实现了IDisposable接口的类,代码如下:

 public class CaryClass :IDisposable
{ public void DoSomething() { Console.WriteLine("Do some thing...."); } public void Dispose() { Console.WriteLine("及时释放资源"); } }
2.我们有两种方式来调用:
2.1.第一种方式,使用Using语句会自动调用Dispose方法,代码如下:
  using (CaryClass caryClass = new CaryClass())
  {
      caryClass.DoSomething();
  }
2.2第二种方式,现实调用该接口的Dispose方法,代码如下:
  CaryClass caryClass = new CaryClass();
   try
    {
       caryClass.DoSomething();               
    }
   finally
    {
       IDisposable disposable = caryClass as IDisposable;
       if (disposable != null) disposable.Dispose();
    }
两种方式的执行结果是一样的,如下图:
finalize2 
2.3.使用try/finally 块比使用 using 块的好处是即使using中的代码引发异常,CaryClass的Dispose方法仍有机
会清理该对象。所以从这里看还是使用try/catch好一些。

二:Disposable 模式
1.在.NET种由于当对象变为不可访问后将自动调用Finalize方法,所以我们手动调用IDisposable接口的Dispose方法
和对象终结器调用的方法极其类似,我们最好将他们放到一起来处理。我们首先想到的是重写Finalize方法,如下:
protected override void Finalize()
{
     Console.WritleLine("析构函数执行...");
}
当我们编译这段代码的时候,我们发现编译器会报如下的错误:
finalize1 
这是因为编译器彻底屏蔽了父类的Finalize方法,编译器提示我们如果要重写Finalize方法我们要提供一个析构函数来
代替,下面我们就提供一个析构函数:
  ~CaryClass()
  {
      Console.WriteLine("析构函数执行...");
  }
实际上这个析构函数编译器会将其转变为如下代码:
protected override void Finalize()
{
   try
   {
     Console.WritleLine("析构函数执行...");
   }
   finally
   {
     base.Finalize();
   }
}
2.然后我们就可以将Dispose方法的调用和对象的终结器放在一起来处理,如下:
public class CaryClass: IDisposable
{
    ~CaryClass()
    {
        Dispose();
    }
    public void Dispose()
    {
        // 清理资源
}
}
3.上面实现方式实际上调用了Dispose方法和Finalize方法,这样就有可能导致做重复的清理工作,所以就有了下面经典
Disposable 模式:
 private bool IsDisposed=false;  
 public void Dispose()  
 {  
     Dispose(true);  
     GC.SupressFinalize(this);  
 }  
 protected void Dispose(bool Diposing)  
 {  
     if(!IsDisposed)  
     {  
         if(Disposing)  
         {  
            //清理托管资源
} //清理非托管资源 } IsDisposed=true; } ~CaryClass() { Dispose(false); }

3.1. SupressFinalize方法以防止垃圾回收器对不需要终止的对象调用 Object.Finalize()。
3.2. 使用IDisposable.Dispose 方法,用户可以在可将对象作为垃圾回收之前随时释放资源。如果调用了 IDisposable.Dispose 方法,此方法会释放对象的资源。这样,就没有必要进行终止。IDisposable.Dispose 应调用 GC.SuppressFinalize 以使垃圾回收器不调用对象的终结器。
3.3.我们不希望Dispose(bool Diposing)方法被外部调用,所以他的访问级别为protected 。如果Diposing为true则释放托管资源和非托管资源,如果 Diposing等于false则该方法已由运行库从终结器内部调用,并且只能释放非托管资源。
3.4.如果在对象被释放后调用其他方法,则可能会引发 ObjectDisposedException。

三:实例解析

1.下面代码对Dispose方法做了封装,说明如何在使用托管和本机资源的类中实现 Dispose(bool) 的常规示例:
public class BaseResource : IDisposable
    {
        // 非托管资源
        private IntPtr handle;
        //托管资源
        private Component Components;
        // Dispose是否被调用
        private bool disposed = false;

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

        protected virtual void Dispose(bool disposing)
        {
           
            if (!this.disposed)          
            {               
                if (disposing)
                {
                    // 释放托管资源
                    Components.Dispose();
                }
                // 释放非托管资源,如果disposing为false, 
                // 只有托管资源被释放
                CloseHandle(handle);
                handle = IntPtr.Zero;
                // 注意这里不是线程安全的
            }
            disposed = true;
        }

        // 析构函数只会在我们没有直接调用Dispose方法的时候调用
        // 派生类中不用在次提供析构函数
        ~BaseResource()
        {
            Dispose(false);
        }

        // 如果你已经调用了Dispose方法后在调用其他方法会抛出ObjectDisposedException
        public void DoSomething()
        {
            if (this.disposed)
            {
                throw new ObjectDisposedException();
            }
        }
    }

    
    public class MyResourceWrapper : BaseResource
    {
        // 托管资源
        private ManagedResource addedManaged;
        // 非托管资源
        private NativeResource addedNative;
        private bool disposed = false;
       
        public MyResourceWrapper()
        {           
        }

        protected override void Dispose(bool disposing)
        {
            if (!this.disposed)
            {
                try
                {
                    if (disposing)
                    {                        
                        addedManaged.Dispose();
                    }
                    
                    CloseHandle(addedNative);
                    this.disposed = true;
                }
                finally
                {                   
                    base.Dispose(disposing);
                }
            }
        }
    }

2.使用CLR垃圾收集器,您不必再担心如何管理对托管堆分配的内存,不过您仍需清理其他类型的资源。托管类通过
IDisposable 接口使其使用方可以在垃圾收集器终结对象前释放可能很重要的资源。通过遵循 disposable 模式并且留
意需注意的问题,类可以确保其所有资源得以正确清理,并且在直接通过 Dispose 调用或通过终结器线程运行清理代码时
不会发生任何问题。

分享到:
评论

相关推荐

    spring.net中文手册在线版

    4.5.1.2.IDisposable接口和destroy-method属性 4.5.2.让对象了解自己的容器 4.5.2.1.IObjectFactoryAware接口 4.5.2.2.IObjectNameAware接口 4.5.3.IFactoryObject接口 4.6.抽象与子对象定义 4.7.与IObjectFactory...

    .NET 命名空间类名判断

    本文将深入探讨如何在.NET环境中进行命名空间的选择、类的识别以及对特定类是否实现IDisposable接口的检查。 **命名空间的概述** 命名空间是.NET框架中用于组织代码的逻辑容器,它有助于避免类名冲突,尤其是在大型...

    vb.net directshow.net 摄像头采集

    在VB.NET中使用DirectShow.NET,首先要进行必要的引用设置。在你的项目中,右键点击“引用”,选择“添加引用”,然后在“浏览”选项卡中找到DirectShowLib.dll文件并添加。这样,你就可以在代码中导入DirectShowLib...

    asp.net与数据库结合实现省市选择

    这通常与IDisposable接口一起使用,因为数据库连接类(如SqlConnection)实现了这个接口。 在ASP.NET中,我们通常使用ADO.NET组件,如SqlConnection、SqlCommand、SqlDataReader等,来与SQL Server数据库进行交互。...

    asp.net中操作Excel助手相关代码

    这是为了在后续操作中使用该路径构建OLEDB连接字符串。 3. 属性 类中有几个属性,包括ConnectionString(用于获取OLEDB连接字符串)、Connection(用于获取数据库连接)、Hdr(HDR的get/set属性)和Imex(IMEX的...

    .net性能优化宝典

    **1.1.3 实现IDisposable接口** 对于需要手动释放资源的情况,实现`IDisposable`接口是一种常见的做法。通过这种方式,可以确保资源在不再需要时能够被及时释放。 #### 字符串操作 **1.2.1 使用StringBuilder做...

    .Net经典面试题目.Net技巧

    面试中遇到的.NET相关问题涵盖了.NET框架的多个核心概念,以下是这些知识点的详细解析: 1. **CLR、CTS、CLS** - **CLR(Common Language Runtime)**:是.NET Framework的核心部分,它提供了一个执行环境,负责...

    .net实体操作类

    在提供的文件信息中,我们可以看到描述了一个名...这些知识点对于想要在.NET项目中使用ORM工具进行高效数据操作的开发者来说是非常重要的。掌握这些内容,可以帮助开发者编写出更高效、可维护性更好的数据访问层代码。

    ASP.Net事务和异常处理

    压缩包中的源代码可能包含了实际的ASP.NET事务和异常处理示例,例如,如何在ASP.NET MVC或Web Forms项目中使用TransactionScope,或者如何在C#代码中实现try-catch-finally结构。通过分析这些代码,你可以更深入地...

    ASP.NET提高代码执行效率

    对于持有非托管资源(如数据库连接、GDI句柄)的对象,应实现IDisposable接口,以确保在不再使用时能立即释放资源。使用using语句可以方便地管理这些资源,它会自动调用Dispose方法并在适当的时候释放资源。 1.2 ...

    asp.net开发Thread问题集

    // 实现IDisposable接口,用于资源清理 public void Dispose() { if (thdSubThread != null && thdSubThread.IsAlive) { StopThread(); thdSubThread.Join(); } } } ``` 通过这个类,你可以创建线程实例,...

    企业真实.Net_面试题大全(有答案)

    C#中使用`new`修饰符来隐藏基类的成员,而使用`override`来重写基类的虚函数。 - ASP.NET中的代码隐藏文件,如用C#编写,将具有`.aspx.cs`扩展名(而不是`.ascx.cs`),这些文件包含了Web窗体的业务逻辑,并且所有...

    C#.NET教程(基础)

    - **析构函数**:用于在对象被销毁前执行清理操作,但C#中通常使用`IDisposable`接口代替。 ### 4. 数据类型 C#中的数据类型分为值类型和引用类型: - **值类型**:如整型(int)、浮点型(float)、布尔型(bool)等,...

    .NET 性能优化方法总结

    **1.1.3 实现IDisposable接口** 对于那些需要手动管理的非托管资源(如文件句柄、数据库连接等),应该通过实现`IDisposable`接口来提供显式的资源清理机制。这样可以在不再需要这些资源时及时释放它们,避免资源...

    ado.net学习笔记

    1. **资源管理**:ADO.NET 中的对象实现了 `IDisposable` 接口,因此在使用完毕后应该释放资源。通常推荐使用 `using` 语句来确保资源被正确释放。 ```csharp using (SqlConnection connection = new ...

    【传智播客.Net培训—ADO.Net】2别忘了Using.rar

    在.NET中,`Using`块是用于管理那些实现了`IDisposable`接口的对象,如数据库连接、文件流等。这个关键字确保了在代码块结束时,即使在出现异常的情况下,也会调用对象的`Dispose`方法,释放所占用的资源。在ADO.NET...

    Accp5.0 s2 深入.NET第四章 理论答案

    - 资源管理:了解IDisposable接口和using语句在资源清理中的作用。 - XML和JSON:掌握XML和JSON的序列化和反序列化,以及在.NET中的操作方式。 - ADO.NET:学习如何使用.NET访问数据库,包括DataTable、DataSet、...

    点.net面试题及答案

    2. 遍历访问对象:在.NET中,若要使用`foreach`循环遍历,对象需实现`IEnumerable`接口或者拥有`GetEnumerator`方法。 3. C#三元运算符:`? :`是C#中的三元运算符,用于简洁地表示条件表达式。 4. 装箱与拆箱:当...

Global site tag (gtag.js) - Google Analytics