频 道 直 达 - 新闻 - 读书 - 培训 - 教程 - 前沿 - 组网 - 系统应用 - 安全 - 编程 - 存储 - 操作系统 - 数据库 - 服务器 - 专题 - 产品 - 案例库 - 技术圈 - 博客 - BBS
51CTO.COM_中国领先的IT技术网站
找资料:

完成器(Finalizer)、程序集名、方法信息的问题解答

作者: 出处:中国IT实验室  (  ) 砖  (  ) 好  评论 ( ) 条  进入论坛
更新时间:2006-10-20 16:34
关 键 词:完成器  程序  方法  FAQ
阅读提示:本文是关于VC.NET中关于完成器(Finalizer)、程序集名、方法信息的部分问题解答。供网友参考!

问题:在我的类中何时需要实现一个完成器?我是否一定要实现完成器,或者只是在我控制着非托管资源时才需要实现它?我是否一定要在我的完成器中实现IDisposable接口?反之又是如何的呢?

答案:完成器只是在你控制了需要被清除的资源的才需要实现。举个例子,FileStream控制了一个本地的文件句柄并且实现了一个完成器,这样也就保证了FileStream被垃圾回收器回收时能释放这个句柄。不幸的是,完成器给垃圾回收带来了一定意义上的负担,因此应仅在必要时才使用完成器。

IDisposable接口的实现表明你的类控制了需要被释放的资源,并且允许你的类用户决定是否要释放它们。因此,任何一个类实现了完成器就一定实现了IDisposable接口(如果垃圾回收器能自动释放资源,那么也应该允许开发者显式地调用某个方法来完成同样的工作)。但这并不是绝对的:并不是所有实现了IDisposable接口的类都要实现一个完成器。

设想一下,我的托管类有一个FileStream类型的私有成员。FileStream控制了一个非托管的资源并且实现了IDisposable接口和完成器。当对该实例不再有引用时,FileStream就变成无法访问的与可终结的了。对我的类而言,它没有理由在注册对象队列里等待终结,因为内嵌的FileStream的实例已经被注册了。另一方面,考虑到我的类应该为用户提供方法以立即释放它所控制的资源,无论这种方法是直接地还是间接的,因此我的类应当实现IDisposable接口。我的Dispose()实现很简单,它只是简单地调用了FileStream的Dispose()方法。切记:尽管如此,你也需要特别小心地释放共享资源(比如正被别的实例使用的资源)。如果你编写一个类从外部释放资源而使该资源可用,请确保你的文档在这个主题上是清晰明确的,这样其他人就知道是否正在移交他们给你的那些资源的控制权了。

如果你的类不需要实现完成器,在你的Dispose()方法中应当调用GC.SuppressFinalize()方法,以确保系统不会去调用你的实例的完成器。这样你的实例同时也会从待终结对象集合中被删除,从而减轻了垃圾回收器在回收过程中的负担。贯穿于Microsoft.NET Framework中常见的实现模式是给Dispose()方法添加一个Boolean(逻辑)类型的参数。这个Boolean类型的参数指示这个类是否因IDisposable.Dispose()方法被调用或者完成器在运行而正在被释放(完成器与IDisposable.Dispose()都是委托到该方法上的)。如果确定它要被释放,GC.SuppressFinalize()就要被调用。如果是通过完成器被释放,就要避免再使用你的类中实现完成器的托管成员,因为它们可能已经被终结了。

Figure 1提供了一些指导性的说明,帮助你在合适的时候在你的类中实现这些结构。

问题:我希望把一些关系到应用程序性能的操作建立在计算机可用内存的基础上。如何才能最简单地从操作系统获取这些信息?

答案:尽管我知道获取这类信息其他的一些方法,但当我发现WMI(Windows Management Instrumentation,Windows管理规范)时,我发现它才是完成这类工作最佳的方式。Win32_OperatingSystem类提供了关于操作系统事件的丰富信息,System.Management命名空间则提供了大量的类来访问WMI的数据。你可以使用Figure 2中的ManagementObjectSearcher类来查询Win32_OperatingSystem.TotalVisibleMemorySize的值。因为ManagementObjectCollection(由ManagementObjectSearcher返回)没有公开访问集合内部元素的方法,因此我使用了一个foreach循环来枚举出其中的每一个成员。而且因为我只关心其中的一个值,所以我在第一次枚举完成后就停止了循环。

注意TotalVisibleMemorySize返回的值可能并不是当前的物理内存总量,而是向操作系统报告可利用的内存量。你可以从Win32_OperatingSystem(Win32_OperatingSystem)这个WMI类中学会更多有用的东西。

问题:我尝试着在未将程序集装载入我的AppDomain的情况下,获取该程序集的完全限定名。这可能做到吗?

答案:绝对能!System.Reflection.AssemblyName类有一个static类型的方法GetAssemblyName(),这可以返回磁盘上一个程序集的名称AssemblyName。这个方法只是简单地打开这个程序集文件,而不会将它装载入AppDomain。下面的这段代码,就能在控制台上输出从命令行传入的路径参数对应程序集的完全限定名:

static void Main(string [] args)
{ if (args.Length > 0) 
{   try   
{ AssemblyName a = AssemblyName.GetAssemblyName(args[0]);
Console.WriteLine(a.Fullname);   
}  
catch(Exception exc)  
{  
Console.WriteLine(exc.Message);  

}
}

注意:同样的技巧也可以用到本地托管的DLL或者EXE上。你可以在这些文件上挨个地试一试。如果没有异常被抛出,并且返回了一个有效的名字,那么这个文件就是托管的。当然,这种方式在所有的本地文件都触发异常的情况下也存在一些性能上的缺陷。当然还有另一种方法,它不依赖于反射,也不需要装载Portable Executable (PE)。而是通过分析DLL或者EXE的PE头中某个标识位是否被置位,由此确定它是否是托管的。Managed Extensions for C++ requently Asked Questions中有实现这种方法的C++代码,等价的C#代码请参见Figure 3。

问题:在我的C#应用程序里有一大堆的foreach循环。当我检查编译器生成的MSIL文件时(Microsoft intermediate language,Microsoft中间语言)时,我发现其中有的循环被嵌入了一个try/finally块,但我的源代码里并没有使用啊?它们为什么会在这里出现?

答案:问得好!记住foreach循环是用来枚举那些集合型的数据的。它是通过获取一个枚举器后,使用枚举器的MoveNext操作来实现遍历,并在Current数据属性中返回集合中的当前项。这个枚举器本身则是通过调用该集合对象的GetEnumerator()方法获得的。因为枚举器可能实现了IDisposable接口,所以C#的编译器需要在枚举完成后将其释放。为此,编译器会将这个循环放入一个try块,然后试着在finally块中释放这个枚举器。如果利用GetEnumerator ()方法返回的枚举器实现了IDisposable接口,编译器就会生成一个类似下面这样的finally块:

((IDisposable)enumerator).Dispose();

如果枚举器没有实现IDisposable接口,那么编译器仍然不得不试着释放这个对象,因为不能确定枚举器的派生类型(从这个函数返回的)没有实现IDisposable接口。于是,编译器又会生成类似这样的一个finally块:

IDisposable disposable = enumerator as IDisposable;if (disposable != null) disposable.Dispose();

因为大多数的可枚举类都有一个GetEnumerator()方法以返回IEnumerator接口(没有实现IDisposable接口),所以上述的代码只是编译器生成结果的一般情形。在少数情况下,当返回的类型没有实现IDisposable接口而且是密封的(指不能从其再派生新的类)时,编译器就不需要实现一个try/finally块了——因为没有什么需要释放的了。

问题:我希望能枚举出一个对象层中的所有对象,但是我不知道如何才能避免很有可能产生的循环引用。.NET Framework对此提供了什么帮助没有?

答案:遍历一个对象层中的所有对象并以某种形式将它们序列化,非常类似于完成一个BinaryFormatter那样的远程格式化程序。对此,.NET Framework也提供了一些很有用的类来完成这类工作。

System.Runtime.Serialization命名空间中的ObjectIDGenerator类可以看作是一张专门化的表,用于跟踪对象并赋与每个对象一个唯一的标识符。你可以通过这个类查询对象的ID——它既可以向你提供继存对象的ID,也可以为一个未出现的对象生成一个新的ID。正因它能防止你两次遍历同一个对象,所以你可以很容易地避免循环引用了。

(责任编辑 火凤凰 sunsj@51cto.com  TEL:(010)68476636-8007)


发表
查看
我也说两句

匿名发表

(如果看不清请点击图片进行更换)


中 国 领 先 的 IT 技 术 网 站 ·
技 术 成 就 梦 想
·Java基础教程 (查看52371次)
·UML类图详解 (查看46839次)
·Java编程开发手册 (查看25118次)
·UML统一建模语言 (查看24099次)
·C#技术开发指南 (查看22406次)
·Java编程开发手册 (1195个砖)
·Java基础教程 (429个砖)
·C#技术开发指南 (304个砖)
·PB开发教程 (220个砖)
·.NET开发手册 (217个砖)
·Java编程开发手册 (653个好)
·Java基础教程 (569个好)
·.NET开发手册 (251个好)
·PB开发教程 (209个好)
·Delphi开发技术手册 (174个好)
订阅技术快讯
电子杂志下载
名称:网络安全精品应用黄皮书
简介:《2007精品网络安全黄皮书》包括了9个大类24个小类, 800余篇文章,内容包含了熊猫烧香病毒、DDOS攻击、ARP病等热点问题的介绍及解决方案。从病毒查杀、防范、系统、数据等各方面的安全设置到黑客技术的了解、防范,涉及到了安全应用的全部领域, 由浅至深内容全面。
名称:Vista精品应用黄皮书
简介:《Vista精品应用黄皮书》囊括了Vista的各方面内容。此次的精简版,是将里面的内容做了提取,便于用户下载和使用。内容包含了各种Vista的安装与实施、技巧与解析以及各种Vista相关学习文档和相关软件的安全下载。该电子书是了解和应用Vista人员必备的工具手册,并且也是第一本
名称:2006中国IT论坛精品集合
简介:本书由“51CTO论坛推广联盟”制作完成。书中所有内容均来自各联盟成员的论坛(网站)。制作本书的目的是为了集中大家的优势资源,将更多更精彩的内容带给广大技术爱好者。本书是联盟成立以来制作的第一本书。
关键字阅读
频道精选
主编信箱 热线:010-66476606 告诉我们您想看的:专题 文章
关于我们 | 诚聘英才 | 联系我们 | 网站大事 | 意见反馈 | 网站地图
Copyright©2005-2007 51CTO.COM 版权所有