C#中的解构器[翻译]

C#中的解构器

引言

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

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

解构器

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

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

 

~ MyClass()
{
// 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()处移除注释。编译并运行它。现在,你能看到解构器在控制台中被执行了。

 

实现IDisposable接口

 

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

 

class MyClass:IDisposable
{
public void 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语句定义了哪个对象将被清除的一个范围。

时间: 2016-04-30

C#中的解构器[翻译]的相关文章

解析JavaScript的ES6版本中的解构赋值_基础知识

什么是解构赋值? 解构赋值允许你使用类似数组或对象字面量的语法将数组和对象的属性值赋给一系列变量.这个语法非常简洁,而且比传统的属性访问更加清晰. 在不使用解构赋值的情况下,访问数组的前三项: var first = someArray[0]; var second = someArray[1]; var third = someArray[2]; var first = someArray[0]; var second = someArray[1]; var third = someArray

Python构造函数及解构函数介绍_python

python 有一个相应的特殊解构器(destructor)方法名为__del__().然而,由于python具有垃圾对象回收机制(靠引用计数),这个函数要直到该实例对象所有的引用都被清除掉后才会被执行.python中的解构器是在实例释放前提供特殊处理功能方法,它们通常没有被实现,因为实例很少被显式释放. 在下面的例子中,我们分别创建(并覆盖) __init__()和__del__()构造器及解构函数,然后,初始化类并给同样的对象很多别名.id()内建函数可用来确定引用同一对象的三个别名.最后一

ECMAScript 6之变量的解构赋值

1,数组的解构赋值 基本用法 ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring). 以前,为变量赋值,只能直接指定值. var a = 1; var b = 2; var c = 3; ES6允许写成下面这样. var [a, b, c] = [1, 2, 3]; 上面代码表示,可以从数组中提取值,按照对应位置,对变量赋值. 本质上,这种写法属于"模式匹配",只要等号两边的模式相同,左边的变量就会被赋予对应的值.下面是一些使用嵌套数

话说模式匹配(3) 模式匹配的核心功能是解构

http://www.artima.com/scalazine/articles/pattern_matching.html 这篇文章是odersky谈scala中的模式匹配的一段对话,我做了部分片段翻译(不是连贯的): 模式可以嵌套,就像表达式嵌套,你可以定义深层的模式,通常一个模式看起来就像一个表达式.它基本上就是同一类事情. 它看起来像一个复杂的对象树构造表达式,只是漏掉了new关键字.事实上在scala当你构造一个对象,你不需要new关键字 然后你可以在一些地方用变量做站位符替代对象树上

SpringMVC 中的Interceptor 拦截器(HandlerInteceptor)

作用 一切请求都可以进去拦截,然后添加前后的处理逻辑 有点像是AOP 可以用在 1.权限检查:如登录检测,进入处理器检测检测是否登录,如果没有直接返回到登录页面: 2.像12306 那样子判断当前时间是否是购票时间. 3.日志记录:记录请求信息的日志,以便进行信息监控.信息统计.计算PV(Page View)等. 4.性能监控:有时候系统在某段时间莫名其妙的慢,可以通过拦截器在进入处理器之前记录开始时间,在处理完后记录结束时间,从而得到该请求的处理时间(如果有反向代理,如apache可以自动记录

C++继承後的建构函式与解构函式

若您继承了某个类别之后,当您在生成衍生类别的物件时若不指定参数,无参数的预设建构子会被执行,而基础类别的无参数预设建构子也会被执行,所以基于这种特性,通常预设建构子中会撰写一些通用的成员状态初始,例如设定一些预设值. 如果继承之后,您要使用衍生类别生成物件,在生成物件时指定参数,并同时执行基底类别中的某个参数建构子,您可以使用 : 运算子 例如: // Point2D类别 class Point2D { public: Point2D() { _x = 0; _y = 0; } Point2D(

清晰解构画面内容 从绘画看设计“层”的概

在历代的视觉风潮中,绘画与设计在方方面面有着关联("设计"为外来词,在传统中国中,对应的词是"经营"),很多时候都是你中有我我中有你的局面,西方美术史上的文艺复兴.新艺术运动就是其中最好的例子.身为插画师以及设计师,在这两者之间游走,有着许多体会.其中对于画面"层"的认知,尤为深刻. 一般在视觉中"层"无论在画面抑或设计手法上都呈现出明显而重要的作用,用"层"的概念去看待作品,不需要过多的分析,都能很直观的

刚接触APP设计的同学必读!APP的解构与重构

大学美术史(选修)的第一堂课上,那老师开门见山的说:知道大家认真听课的不多,下节课能来多少也不知道,这里介绍一个自学阅读的办法,方便你们抱佛脚,在我看来大部分书籍讲的实质性内容都很少,这就需要拧干提纯,发现一本书有用之处最有效的方法是看目录,大家能消化掉美术史这本书的目录就能考 60分了.对于其它休闲类书籍你大可从目录里面选择自己有兴趣的章节阅读,感兴趣的章节对你来说就是干货,整个目录你都不感兴趣的话,那这本书就杯具了- 不知道其他同学的感触,我后来阅读一直用这个方法,此技巧甚至可以列入我大学所

QuickBMS通用解包器使用指南

从本质上来说,QuickBMS只是一个通用解包器引擎,由Luigi Auriemma开发并维护,虽然QuickBMS简单而高效,技术组也经常使用QuickBMS来解包游戏的资源文件,但任何技术都存在一个入门门槛,而本篇以及后续教学笔记就试图为同学们拆除这个门槛.          那么,进入QuickBMS世界的门槛有多高呢?我将使用什么方法来拆除这个门槛呢?    第一节 QuickBMS使用方法            QuickBMS的使用方法没有任何难度,其步骤如下所示:    1.使用鼠