Microsoft C#编码规范 下载本文

Page 41

} }

?一定请在类型应该为释放非托管资源负责,且自身没有终结器的情况下,将该类型定义为可终结的。当实现终结器时,简单的调用Dispose(false),并将所有资源清理逻辑放入 Dispose(bool disposing)方法。

// C# sample:

publicclassComplexResourceHolder : IDisposable { ...

~ComplexResourceHolder() {

Dispose(false); }

protectedvirtualvoid Dispose(bool disposing) { ... } }

?一定请谨慎的定义可终结类型。仔细考虑任何一个您需要终结器的情况。带有终结器的实例从性能和复杂性角度来说,都需付出不小的代价。

?一定请为每一个可终结类型实现基础Dispose 模式。该模式的细节请参考先前章节。这给予该类型的使用者以一种显式的方式去清理其拥有的资源。

?您应该在终结器即使面临强制的应用程序域卸载或线程中止的情况也必须被执行时,创建并使用临界可终结对象(一个带有包含了CriticalFinalizerObject的类型层次的类型)。

?一定请尽量使用基于SafeHandle或SafeHandleZeroOrMinusOneIsInvalid(对于 Win32 资源句柄,其值如果为0或者-1 ,则代表其为无效句柄)的资源封装器,而不是自己来编写终结器。这样,我们便无需终结器,封装器会为其资源清理负责。安全句柄实现了IDisposable接口,并继承自CriticalFinalizerObject,所以即使面临强制的应用程序域卸载或线程中止,终结器的逻辑也会被执行。

? 2016 Microsoft Corporation. All rights reserved.

Page 42

///

/// Represents a wrapper class for a pipe handle. ///

[SecurityCritical(SecurityCriticalScope.Everything),

HostProtection(SecurityAction.LinkDemand, MayLeakOnAbort = true), SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)] internalsealedclassSafePipeHandle : SafeHandleZeroOrMinusOneIsInvalid {

private SafePipeHandle() : base(true) { }

public SafePipeHandle(IntPtr preexistingHandle, bool ownsHandle) : base(ownsHandle) {

base.SetHandle(preexistingHandle); }

[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success), DllImport(\, CharSet = CharSet.Auto, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)]

privatestaticexternbool CloseHandle(IntPtr handle);

protectedoverridebool ReleaseHandle() {

return CloseHandle(base.handle); } }

///

/// Represents a wrapper class for a local memory pointer. ///

[SuppressUnmanagedCodeSecurity,

HostProtection(SecurityAction.LinkDemand, MayLeakOnAbort = true)]

internalsealedclassSafeLocalMemHandle : SafeHandleZeroOrMinusOneIsInvalid {

public SafeLocalMemHandle() : base(true) { }

public SafeLocalMemHandle(IntPtr preexistingHandle, bool ownsHandle) : base(ownsHandle) {

base.SetHandle(preexistingHandle);

? 2016 Microsoft Corporation. All rights reserved.

Page 43

}

[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success), DllImport(\, CharSet = CharSet.Auto, SetLastError = true)] privatestaticexternIntPtr LocalFree(IntPtr hMem);

protectedoverridebool ReleaseHandle() {

return(LocalFree(base.handle) == IntPtr.Zero); } }

?一定不要在终结器代码路径内访问任何可终结对象,因为这样会有一个极大的风险:它们已经被终结了。例如,一个可终结对象A ,其拥有一个指向另一个可终结对象B的对象,在A的终结器内并不能很安心的使用B,反之亦然。终结器是被随机调用的。

但是操作拆箱的值类型字段是可以接受的。

同时也请注意,在应用程序域卸载或进程退出的某些时间点上,存储于静态变量的对象会被回收。如果Environment.HasShutdownStarted返回True,则访问引用了一个可终结对象的静态变量(或调用使用了存储于静态变量的值的静态函数)可能会不安全。

?一定不要从终结器的逻辑内抛出异常,除非是系统严重故障。如果从终结器内抛出异常,CLR可能会停止整个进程来阻止其他终结器被执行,并阻止资源以一个受控制的方式释放。

3.12.4 重写 Dispose

如果您继承了实现IDisposable接口的基类,您必须也实现IDisposable接口。记得要调用您基类的 Dispose(bool)。

publicclassDisposableBase : IDisposable {

~DisposableBase() {

Dispose(false); }

? 2016 Microsoft Corporation. All rights reserved.

Page 44

publicvoid Dispose() {

Dispose(true);

GC.SuppressFinalize(this); }

protectedvirtualvoidDispose(bool disposing) { // ... } }

publicclassDisposableSubclass : DisposableBase {

protectedoverridevoidDispose(bool disposing) { try {

if(disposing) {

// Clean up managed resources. }

// Clean up native resources. } finally {

base.Dispose(disposing); } } }

3.12.5 P/Invoke

?一定请在编写P/Invoke签名时,查阅P/Invoke交互操作助理以及http://pinvoke.net。

?您可以使用IntPtr来进行手动封送处理。虽然会牺牲易用性,类型安全和维护性,但是通过将参数和字段声明为IntPtr,您可以提升性能表现。有时,通过Marshal类的方法来执行手动封送处理比依赖默认交互操作封送处理的性能更高。举例来说,如果有一个字符串的大数组需要被传递跨越交互操作边界,但是托管代码只需其中几个元素,那么您就可以将数组声明为IntPtr,手动的访问所需的几个元素。

? 2016 Microsoft Corporation. All rights reserved.