C#中的析构函数

函数

引言

  在企业应用开发世界,性能,灵活性和安全性是最重要的。我作为一个VC++程序员开始我的职业生涯,并且在一个晴朗的早晨,我被转到了Web开发部。像每个C++程序员一样,我也很失落。我想每个像Tom,Dick甚至Harry能用HTML编程。然而,不久我就发现真正的挑战是生产高性能的,灵活的可靠的应用程序。综上所述,Web环境松耦合的,不分国界的本质将使你永远神往。

    为了制作高性能的灵活的应用程序,用最优化的方式使用你的资源是至关重要的。一个技巧是尽可能晚地使用你的资源并且在使用后尽快释放它。我在这里的意图是描述在C#中的对象清除机制。

解构器

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

一个类只能有一个解构器。

解构器不能被继承或重载。

解构器不能被调用。他们是自动被(编译器)调用的。

解构器不能带修饰或参数。

下面是类MyClass解构器的一个声明:

~ MyClass()

   // Cleaning up code goes here
}

 

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

protectedoverridevoid 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{ publicstaticvoid 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 BCreating CObject Created Press enter to Destroy itDestroying CDestroying BDestroying A    所以,如果一旦你使用完对象你就想调用解构器,你该怎么做?有两个方法:调用垃圾搜集器来清理。

实现IDisposable的Dispose方法。

调用垃圾搜集器

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

实现IDisposable接口

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

class MyClass:IDisposable

{publicvoid Dispose() {   //implementation }}  当我们实现了IDisposable接口时,我们需要规则来确保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)来做清理工作,并且所有的清理代码都仅写在这个方法中。这个方法被解构器和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语句定义了哪个对象将被清除的一个范围。

 

时间: 2024-08-02 19:29:13

C#中的析构函数的相关文章

全面解析C++中的析构函数_C 语言

"析构函数"是构造函数的反向函数.在销毁(释放)对象时将调用它们.通过在类名前面放置一个波形符 (~) 将函数指定为类的析构函数.例如,声明 String 类的析构函数:~String(). 在 /clr 编译中,析构函数在释放托管和非托管资源方面发挥了特殊作用. 析构函数通常用于在不再需要某个对象时"清理"此对象.请考虑 String 类的以下声明: // spec1_destructors.cpp #include <string.h> class

详解C++编程中的析构函数_C 语言

C++析构函数 创建对象时系统会自动调用构造函数进行初始化工作,同样,销毁对象时系统也会自动调用一个函数来进行清理工作(例如回收创建对象时消耗的各种资源),这个函数被称为析构函数. 析构函数(Destructor)也是一种特殊的成员函数,没有返回值,不需要用户调用,而是在销毁对象时自动执行.与构造函数不同的是,析构函数的名字是在类名前面加一个"~"符号. 注意:析构函数没有参数,不能被重载,因此一个类只能有一个析构函数.如果用户没有定义,那么编译器会自动生成. 析构函数举例: #inc

解析C++中虚析构函数的作用_C 语言

我们知道,用C++开发的时候,用来做基类的类的析构函数一般都是虚函数.可是,为什么要这样做呢?下面用一个小例子来说明:    有下面的两个类: 复制代码 代码如下: class ClxBase{public:    ClxBase() {};    virtual ~ClxBase() {};    virtual void DoSomething() { cout << "Do something in class ClxBase!" << endl; };}

在基类中的析构函数声明为virtual

1 #include <iostream> 2 using namespace std; 3 4 class Father 5 { 6 public: 7 ~Father() 8 { 9 cout << "Father's Desconstruct Called. " << endl; 10 } 11 }; 12 13 class Son: public Father 14 { 15 public: 16 ~Son() 17 { 18 cout &l

c++ 指针 析构函数-c++中析构函数中的指针问题

问题描述 c++中析构函数中的指针问题 在c++一个类中的数据成员是指针.在这个类中的析构函数指针为什么要先删除.然后再定义成空指针? 解决方案 先删除主要是为了释放掉分配在堆的内存数据,防止内存泄漏,然后赋值为NULL主要是防止野指针导致程序崩溃,这样做体现出程序的健壮性,我用一个形象低俗的例子记住了这一点,把赋值为NULL想象成上完厕所记得擦屁股--这样程序就不会出现野指针了,因为野指针很大可能会是导致项目失败的关键 解决方案二: 因为这个指针是new出来的,如果不delete掉,会引起内存

详解C++中如何将构造函数或析构函数的访问权限定为private_C 语言

今天面试被问到了这个单例模式常用到的技术手段,下面进行分析:         很多情况下要求当前的程序中只有一个object.例如一个程序只有一个和数据库的连接,只有一个鼠标的object.通常我们都将构造函数的声明置于public区段,假如我们将其放入private区段中会发生什么样的后果?这意味着什么?         当我们在程序中声明一个对象时,编译器为调用构造函数(如果有的话),而这个调用将通常是外部的,也就是说它不属于class对象本身的调用,假如构造函数是私有的,由于在class外

理解finalize()-析构函数替代者

函数 理解finalize()-析构函数替代者   在许多方面,Java 类似于 C++.Java 的语法非常类似于 C++,Java 有类.方法和数据成员:Java 的类有构造函数: Java 有异常处理.       但是,如果你使用过 C++ 会发现 Java 也丢掉一些可能是你熟悉的特性.这些特性之一就是析构函数.取代使用析构函数,Java 支持finalize() 方法.       在本文中,我们将描述 finalize() 与 C++ 析构函数的区别.另外,我们将创建一个简单的 A

[FAQ]PHP中的一些常识:类篇

我把一些PHP中类的常识性整理出来,让新手们更容易掌握PHP类,以便能快速看懂那些老大们写的程序,呵呵,不过比较少,希望老大们补充一下或者修正其中错误理解. Q:PHP中的类可以有构造函数么?A:可以,只要函数名和类名相同,这个函数将成为构造函数.在使用 new 操作符来创建一个类的实例时,该构造函数将会自动调用,因此,可以把一些初始化工作放在构造函数里完成. Q:PHP中的类有析构函数么?A:没有.当对象被销毁,使用 unset() 或者简单的脱离范围,析构函数都会自动调用.但 PHP 中没有

漫谈.Net中的自动垃圾收集(Garbage Collection)机制(转)

作者:cornfield漫谈.Net中的自动垃圾收集(Garbage Collection)机制    一直以来,垃圾收集(Garbage Collection)在软件界的名声并不好.很多程序员认为垃圾收集做得不如自己来的直接,高效.这种说法有些时候是对的,一个精心为自己的特定程序设计定制的内存回收方法,肯定比为所有程序提供垃圾回收性能要高.但那对程序员要求甚高,一个项目下来花在内存回收的设计上的时间和精力是很可观的,而稍有不慎便会酿成灾难性的错误,技术再高超的程序员负担不起,整个现代软件工业也