问题:在我的类中何时需要实现一个完成器?我是否一定要实现完成器,或者只是在我控制着非托管资源时才需要实现它?我是否一定要在我的完成器中实现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。下面的这段代码,就能在控制台上输出从命令行传入的路径参数对应程序集的完全限定名:
|
注意:同样的技巧也可以用到本地托管的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)
|
|||
| · 51CTO主编推荐经典专题 · RAID——磁盘阵列基础 · 充电计划之热门IT认证.. · 51CTO技术自测 挑战自.. · CISSP认证成长之路 · AMD Phenom三核处理器.. · 国际文档格式标准开战 · 2007年互联网大会 |
· 我是黑客我怕谁——讲.. · ARP攻击防范与解决方案 · Solaris 10 配置管理 · Solaris基础知识入门 · RIP路由协议专栏 · MPLS路由协议专栏 · OSPF路由协议专栏 · 思科路由器产品 |
||
|
|||
| · Java基础教程 · VPN技术 · ARP攻击防范与解决方案 · SQL Server 2005全解 · SOA 面向服务架构 · SQL Server 2005全解 · Java编程开发手册 · RAID——磁盘阵列基础 |
· 三层交换技术专题 · SQL Server入门到精通 · Windows Server 2003企.. · Windows远程桌面应用 · C#技术开发指南 · VPN技术 · Solaris 10 配置管理 · C#技术开发指南 |
||
|
|||
| · ARP攻击防范与解决方案 · VPN技术 · SQL Server 2005全解 · Java基础教程 · SQL Server入门到精通 · SQL Server 2005全解 · SOA 面向服务架构 · Java编程开发手册 |
· C#技术开发指南 · 三层交换技术专题 · C#技术开发指南 · Windows远程桌面应用 · RAID——磁盘阵列基础 · Windows Server 2003企.. · 邮件服务器专题 · wimax技术与趋势 |
||
| ·DB2 Viper快速入门 ·DB2 9数据库的镜像分割与.. |
·将XML应用程序从DB2 8.x.. ·DB2 9中的pureXML:如何.. |
| ·服务器中的“傻瓜机”在.. ·盖茨也喜欢登录Youtube看.. |
· · |
| ·拯救系统管理员 ·美国选民:我为什么选布什 |
·VMware公司中文命名挑战赛 ·我们真缺乏创新吗? |
| ·J0ker的CISSP之路:复习-.. ·J0ker的CISSP之路:复习-I.. |
·9月第3周安全回顾 内网安.. ·教你几招识别和防御Web网.. |
| · NGN:下一代网络 · 网络访问中断大排查 · FTTx光纤接入 |
· 教你使用Anti ARP Sniff.. · 网络嗅探教程:使用Snif.. · 常见病毒手工清除方法大.. |
| · C++是垃圾语言?! · 2007年IT界七大抄袭事件 · Java实用开发全集 |
· 解析Ajax开发框架 走进A.. · 基于Google Maps与Ajax.. · 基于Google Maps与Ajax.. |
| · 热门 IT 培训认证官方资.. · Ubuntu 中文开源频道 · Solaris基础知识入门 |
· 费力不讨好 数据中心主.. · AMD Phenom三核处理器解.. · 51CTO主编推荐经典专题 |
| · 甲骨文Oracle 11g正式发.. · Oracle数据库开发之PL/S.. · Oracle数据库开发基础教.. |
· 存储2006,一个并购的大.. · IDC宣布浪潮蝉联存储市.. · 双机热备技术 |