一 、

1.using 语句获取一个或多个资源,执行一个语句,然后处置该资源。     
2.using 语句: 
using ( 资源获取 ) 嵌入语句 
3.资源获取: 
局部变量声明 
表达式

资源是实现 System.IDisposable 的类或结构,它包含名为 Dispose 的单个无参数方法。(如:图2)正在使用资源的代码可以调用 Dispose 以指示不再需要该资源。如果不调用 Dispose,则最终将因为垃圾回收而发生自动处置。

如果资源获取的形式是局部变量声明,那么此局部变量声明的类型必须为System.IDisposable 或是可以隐式转换为 System.IDisposable 的类型。如果资源获取的形式是表达式,那么此表达式必须是 System.IDisposable 或是可以隐式转换为 System.IDisposable 的类型。

在资源获取中声明的局部变量必须是只读的,且必须包含一个初始值设定项。

using 语句被翻译成三个部分:获取、使用和处置。资源的使用隐式封闭在包含一个 finally 子句的 try语句中。此 finally 子句处置资源。如果获取了 null 资源,则不进行对 Dispose 的调用,也不引发任何异常。

例如,下列形式的 using 语句

using (R r1 = new R()) { 
r1.F(); 

完全等效于

R r1 = new R(); 
try { 
r1.F(); 

finally { 
if (r1 != null) ((IDisposable)r1).Dispose(); 
}

图1:

    using确保执行IDisposable接口的对象在退出块时立即释放,主要是为了防止忘记关闭数据库连接可能导致的.net可执行程序的各种问题。 

图2:

二、

CLR 有一个垃圾收集GC机制,可以管理内存分配和回收等工作,在绝大多数情况下,程序员只需要new 一个对象,而将销毁这一对象的工作完全交给CLR代劳。

但是,我们所编写的类中使用了非托管的资源,比如文件句柄,用于线程同步的Mutex对象,或者是数据库连接,这些资源应该遵循“即需即建即销毁”的原则,这就是说:需要的时候才创建这些对象,用完之后就马上销毁。

析构函数(destructor) 与构造函数相反,当对象脱离其作用域时(例如对象所在的函数已调用完毕),系统自动执行析构函数。析构函数往往用来做“清理善后” 的工作(例如在建立对象时用new开辟了一片内存空间,应在退出前在析构函数中用delete释放)。

以C++语言为例,析构函数名也应与类名相同,只是在函数名前面加一个波浪符~,例如~stud( ),以区别于构造函数。它不能带任何参数,也没有返回值(包括void类型)。只能有一个析构函数,不能重载。如果用户没有编写析构函数,编译系统会自动生成一个缺省的析构函数,它也不进行任何操作。所以许多简单的类中没有用显式的析构函数。

解构器

我们知道,‘解构器’被用来清除类的事例。当我们在C#中使用解构器是,我们必须记住以下几点:

一个类只能有一个解构器。 
       解构器不能被继承或重载。 
       解构器不能被调用。他们是自动被(编译器)调用的。 
       解构器不能带修饰或参数。 
   下面是类MyClass解构器的一个声明:

 

~ Class()   
{  
    // Cleaning up code goes here  
}

 

程序员不能控制解构器何时将被执行因为这是由垃圾收集器决定的。垃圾收集器检查不在被应用程序使用的对象。它认为这些条件是符合清楚的并且收回它们的内存。解构器也在程序退出时被调用。当解构器执行时其背后所发生的那一幕是解构器隐式调用对象基类的Object.Finalize方法。因此上述解构器代码被隐含转化成:

 

protected override void Finalize()  
{  
    try  
    {  
       // Cleaning up .  
    }  
    finally  
    {  
       base.Finalize();  
    }
}

现在,让我们看一个解构器怎样被调用的例子。我们有三个类A,B和C 。B派生自A,C派生自B。每个类有它们自己的构造器和解构。在类App的main函数中,我们创建C的对象。

 

using System;  
class A  
{  
public A()  
{  
   Console.WriteLine("Creating A");  
}  
~A()  
{  
   Console.WriteLine("Destroying A");  
}  
}  
   
class B:A  
{  
public B()  
{  
   Console.WriteLine("Creating B");  
}  
~B()  
{  
   Console.WriteLine("Destroying B");  
}  
   
}  
class C:B  
{  
public C()  
{  
   Console.WriteLine("Creating C");  
}  
   
~C()  
{  
   Console.WriteLine("Destroying C");  
}  
}  
class App  
{  
public static void Main()  
{  
   C c=new C();  
   Console.WriteLine("Object Created ");  
   Console.WriteLine("Press enter to Destroy it");  
   Console.ReadLine();  
   c=null;  
   //GC.Collect();  
   Console.Read();  
}  
}
  
     正如我们预料的,基类的构造器将会被执行并且程序会等待用户按‘enter’。当这个发生,我们把类C的对象置为null.但解构器没有被执行..!!??正像我们所说的,程序员无法控制解构器何时被执行因为这是由垃圾搜集器决定的。但程序退出时解构器被调用了。你能通过重定向程序的o/p到文本文件来检查这个。我将它输出在这里。注意到基类的解构器被调用了,因为在背后base.Finalize()被调用了。

 

Creating A
Creating B
Creating C
Object Created 
Press enter to Destroy it
Destroying C
Destroying B
Destroying A
 
     所以,如果一旦你使用完对象你就想调用解构器,你该怎么做?有两个方法:

调用垃圾搜集器来清理。

实现IDisposable的Dispose方法。

调用垃圾搜集器

你能通过调用GC.Collect方法强制垃圾搜集器来清理内存,但在大多数情况下,这应该避免因为它会导致性能问题。在上面的程序中,在GC.Collect()处移除注释。编译并运行它。现在,你能看到解构器在控制台中被执行了。

以过上面的分析,我们要明确,当CLR的垃圾收集线程要回收一个定义了析构函数的对象时,它会自动调用其Finalize方法。

析构函数存在的主要目的是释放非托管的资源。但是,对象的析构函数(即Finalize方法)被调用的时机是不可控的,因为它的调用由CLR的垃圾收集机制

负责,出于性能考虑,CLR的垃圾收集线程只是在“它认为是合适的”时机才运行,这样一来就有可能出现对象所占有的非托管资源迟迟不能得到释放的情况。

 

最好有一种方法能够让程序员主动地以完全可控的方式去释放这些非托管的资源,为此.NET提供了一个IDispose接口。

实现IDisposable接口

 

IDisposable 接口包括仅有的一个公共方法,其声明为void Dispose()。我们能实现这个方法来关闭或释放非托管资源如实现了这个接口的类事例所控制的文件,流,和句柄等。这个方法被用做所有任务联合对象的资源释放。当实现了这个方法,对象必须寻求确保所有拥有的资源被继承结构中关联的资源也释放(不能把握,翻不出来)。

 

class MyClass:IDisposable  
{  
     public void Dispose()  
{  
   //implementation  
}  
}

当我们实现了IDisposable接口时,我们需要规则来确保Dispose被适当地调用。

这里有一个问题,CLR不认为IDisposable接口与其他接口有什么不同,因CLR的垃圾收集线程不会主动地询问对象是否实现了IDisposable接口

并自动调用它的Dispose方法。另外,很多可能程序员在开发时会忘记调用对象的Dispose方法,为了避免资源泄露,我们让对象的析构函数也调用Dispose方法,

这就双保险了。如以下代码所示:

联合使用解构器和IDisposable接口

Public class MyClass:IDisposable  
{  
private bool IsDisposed=false;  
public void Dispose()  
{  
   Dispose(true);  
   GC.SupressFinalize(this);  
}  
protected void Dispose(bool Diposing)  
{  
   if(!IsDisposed)  
   {  
   if(Disposing)  
   {  
    //Clean Up managed resources  
   }  
   //Clean up unmanaged resources  
}  
IsDisposed=true;  
}  
~MyClass()  
{  
   Dispose(false);  
}  
}

这个代码框架看上去不太好理解,首先,给类添加一个Dispose(bool),此方法接收一个bool类型的参数,当参数值为true 时,可以编写一些必要的代码来清理托管的资源,比如

调用本对象所引用的其他托管对象的Dispose方法。不管参数值如何,此Dispose(bool)方法都必须完成清理非托管资源的任务。

当用户在应用 程序中显式调用IDisposable接口的Dispose方法时,此方法以true作为实参调用上面定义的虚方法Dispose(bool),并通知CLR的垃圾收集线程,不要再调用此对象的Finalize方法。

如果用户没有显式调用IDisposable接口的Dispose方法,则由CLR的垃圾收集线程负责调用对象的Finalize方法完成资源清理工作,注意这时传入的参数是false,国为finalize方法只负责释放非托管的资源。

~MyClass()

{

Dispose(false);

}

在这里重载了Dispose(bool)来做清理工作,并且所有的清理代码都仅写在这个方法中。这个方法被解构器和IDisposable.Dispose()两着调用。我们应该注意Dispose(bool)没有在任何地方被调用除了在IDisposable.Dispose()和解构器中。

当一个客户调用IDisposable.Dispose()时,客户特意地想要清理托管的和非托管资源,并且因此完成清理工作。有一件你必须注意的事情是我们在清理资源之后立即调用了GC.SupressFinalize(this)。这个方法通知垃圾搜集器不需要调用解构器,因为我们已经做了清理。

注意上面的例子,解构器使用参数false调用Dispose。这里,我们确信垃圾搜集器搜集了托管资源。我们仅仅做非托管资源的清理。

结论

尽管如此我们花费一些时间实现IDisposable接口,如果客户不能合适地调用它们会怎样?为此C#有一个酷的解决方案。‘using’代码块。它看起来像这样:

using (MyClass objCls =new MyClass())  
{  
   
}
 
     当控制从using块通过成功运行到结束或者抛出异常退出时,MyClass的IDispose.Dispose()将会被执行。记住你例示的对象必须实现System.IDisposable接口。using语句定义了哪个对象将被清除的一个范围。

注:
   构造函数与析构函数的区别:
      构造函数和析构函数是在类体中说明的两种特殊的成员函数。
      构造函数的功能是在创建对象时,使用给定的值来将对象初始化。
      析构函数的功能是用来释放一个对象的。在对象删除前,用它来做一些清理工作,它与构造函数的功能正好相反。

C#:using与.net对象销毁的更多相关文章

  1. c# -- 对象销毁和垃圾回收

    有些对象需要显示地销毁代码来释放资源,比如打开的文件资源,锁,操作系统句柄和非托管对象.在.NET中,这就是所谓的对象销毁,它通过IDisposal接口来实现.不再使用的对象所占用的内存管理,必须在某 ...

  2. Perl面向对象(3):解构——对象销毁

    本系列: Perl面向对象(1):从代码复用开始 Perl面向对象(2):对象 Perl面向对象(3):解构--对象销毁 第3篇依赖于第2篇,第2篇依赖于1篇. perl中使用引用计数的方式管理内存, ...

  3. python对象销毁(垃圾回收)

    Python 使用了引用计数这一简单技术来跟踪和回收垃圾. 在 Python 内部记录着所有使用中的对象各有多少引用. 一个内部跟踪变量,称为一个引用计数器. 当对象被创建时, 就创建了一个引用计数, ...

  4. C++临时对象销毁时间

    下面这段代码会输出什么? const char* p = string("hello temprary string").c_str(); cout << p; 下面这 ...

  5. Java 对象销毁

    Java语言拥有一套完整的垃圾回收机制. 何种对象会被java虚拟机视为垃圾.主要包括以下两种情况: (1)对象引用超过其作用范围,则这个对象将被视为垃圾 (2)将对象赋值为null 参考资料:Jav ...

  6. Spring 中,对象销毁前执行某些处理的方法

    通过 @PreDestroy 和 bean 中配置 destroy-method 实现该功能 java 代码中: 1: public class TestClass { 2: private Sche ...

  7. Java的结构之美【2】——销毁对象

    先来看一段代码: import java.util.Arrays; import java.util.EmptyStackException; /** * 2014年6月28日09:31:59 * @ ...

  8. [ Java学习基础 ] Java对象的创建和销毁

    类实例化可生成对象,实例方法就是对象方法,实例变量就是对象属性.一个对象的生命周期包括三个阶段:创建.使用和销毁. 创建对象 创建对象包括两个步骤:声明和实例化. 声明 声明对象与声明普通变量没有区别 ...

  9. C++深度解析教程学习笔记(6)对象的构造和销毁

    1. 对象的初始化 (1)从程序设计的角度看,对象只是变量,因此: ①在栈上创建对象时,成员变量初始化为随机值 ②在堆上创建对象时,成员变量初始化为随机值 ③在静态存储区创建对象时,成员变量初始化为 ...

随机推荐

  1. 验证(C#和正则表达式)

    原文:验证(C#和正则表达式) 我们经常会需要验证字符串的格式,比如密码长度范围.电子邮件格式.固定电话号码和手机号码格式等,这个时候我们经常会需要用到正则表达式.但是正则表达式用起来性能会低一点,所 ...

  2. Spring IOC之 使用JSR 330标准注解

    从Spring 3.0开始,Spring提供了对 JSR 330标准注解的支持.这些注解可以喝Spring注解一样被扫描到.你只需要将相关的Jar包加入到你的classpath中即可. 注意:如果你使 ...

  3. DDD分层架构之领域实体(基础篇)

    DDD分层架构之领域实体(基础篇) 上一篇,我介绍了自己在DDD分层架构方面的一些感想,本文开始介绍领域层的实体,代码主要参考自<领域驱动设计C#2008实现>,另外参考了网上找到的一些示 ...

  4. apache 提示You don't have permission to access /test.php on this server.怎样解决

    把denty改成allow httpd.conf文件中. <Directory "cgi-bin"> AllowOverride None Options None O ...

  5. IIS下配置php5.5

    首先添加IIS. 控制面板-〉程序-〉打开或关闭Windows功能 1. 勾选"Internet 信息服务" 2. 勾选"IIS 管理控制台" Internet ...

  6. Ibatis ISqlMapper工厂类案例

    namespace Model{ public class MapperFactory { //声明一个ISqlMapper接口类型的数据映射器 _mapper,其初始值为null private s ...

  7. [转]【Android】9-patch图片以及例子说明

    1.何为9-patch? NinePatch图片以*.9.png结尾,和普通图片(png图片)的区别是四周多了一个边框(如下图所示): 采用NinePatch图片做背景,可使背景随着内容的拉伸(缩小) ...

  8. Android控件RecyclerView与ListView的异同

    在我的一篇介绍Android新控件RecyclerView的博客(Android L新控件RecyclerView简介)中,一个读者留言说RecyclerView跟ListView之间好像没有什么不同 ...

  9. DropDownListFor的用法

    Asp.Net MVC中DropDownListFor的用法   在Asp.Net MVC中可以用DropDownListFor的方式来让用户选择已定列表中的一个数值.用法不复杂,这里简单做一个记录. ...

  10. MVC中验证码

    MVC中验证码的实现(经常用,记录备用)   一.目录 1.多层架构+MVC+EF+AUTOFAC+AUTOMAPPER: 2.MVC中验证码的实现(经常用,记录备用) 3.Ligerui首页的快速搭 ...