在类型使用者忘记调用 Dispose 的情况下,Dispose 方法,则垃圾回收器将自动调用安全句柄的终结器。
Microsoft.Win32.SafeHandles 命名空间中的以下派生类提供安全句柄:
- SafePipeHandle 类。
- SafeMemoryMappedViewHandle 类。
- SafeNCryptSecretHandle 类。
- SafeRegistryHandle 类。
- SafeWaitHandle 类。
Dispose()
此外,任何非密封类都应具有要实现的附加 Dispose(bool) 重载方法
因此,它具有标准实现:
public void Dispose() { // Dispose of unmanaged resources. Dispose(true); // Suppress finalization. GC.SuppressFinalize(this); }
Dispose 方法(其值为 true)还是来自终结器(其值为 false)。
方法的主体包含两个代码块:
-
无论
disposing参数的值如何,都会执行此块。 -
它释放的托管资源可包括:
-
SafeHandle.Dispose() 实现。
-
相比以非确定性方式回收它们,这样做释放的速度更快。
-
这一点很重要,因为垃圾回收器在终止期间销毁托管对象的顺序是不确定的。
使用安全句柄实现释放模式
using Microsoft.Win32.SafeHandles; using System; using System.Runtime.InteropServices; public class DisposableStreamResource : IDisposable { // Define constants. protected const uint GENERIC_READ = 0x80000000; protected const uint FILE_SHARE_READ = 0x00000001; protected const uint OPEN_EXISTING = 3; protected const uint FILE_ATTRIBUTE_NORMAL = 0x80; private const int INVALID_FILE_SIZE = unchecked((int)0xFFFFFFFF); // Define Windows APIs. [DllImport("kernel32.dll", EntryPoint = "CreateFileW", CharSet = CharSet.Unicode)] protected static extern SafeFileHandle CreateFile( string lpFileName, uint dwDesiredAccess, uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile); [DllImport("kernel32.dll")] private static extern int GetFileSize( SafeFileHandle hFile, out int lpFileSizeHigh); // Define locals. private bool _disposed = false; private readonly SafeFileHandle _safeHandle; private readonly int _upperWord; public DisposableStreamResource(string fileName) { if (string.IsNullOrWhiteSpace(fileName)) { throw new ArgumentException("The fileName cannot be null or an empty string"); } _safeHandle = CreateFile( fileName, GENERIC_READ, FILE_SHARE_READ, IntPtr.Zero, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero); // Get file size. Size = GetFileSize(_safeHandle, out _upperWord); if (Size == INVALID_FILE_SIZE) { Size = -1; } else if (_upperWord > 0) { Size = (((long)_upperWord) << 32) + Size; } } public long Size { get; } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (_disposed) { return; } // Dispose of managed resources here. if (disposing) { _safeHandle?.Dispose(); } // Dispose of any unmanaged resources not wrapped in safe handles. _disposed = true; } }
DisposeAsync()
无参数的 DisposeAsync() 方法在 await using 语句中隐式调用,它具有标准实现:
public async ValueTask DisposeAsync() { // Perform async cleanup. await DisposeAsyncCore(); // Dispose of managed resources. Dispose(false); // Suppress finalization. GC.SuppressFinalize(this); }
因此,调用 Dispose(false) 而非 Dispose(true)。
using System; using System.Text.Json; using System.Threading.Tasks; public class ExampleAsyncDisposable : IAsyncDisposable, IDisposable { // To detect redundant calls private bool _disposed = false; // Created in .ctor, omitted for brevity. private Utf8JsonWriter _jsonWriter; public async ValueTask DisposeAsync() { await DisposeAsyncCore(); Dispose(false); GC.SuppressFinalize(this); } protected virtual async ValueTask DisposeAsyncCore() { // Cascade async dispose calls if (_jsonWriter != null) { await _jsonWriter.DisposeAsync(); _jsonWriter = null; } } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (_disposed) { return; } if (disposing) { _jsonWriter?.Dispose(); // TODO: dispose managed state (managed objects). } // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below. // TODO: set large fields to null. _disposed = true; } }
堆叠的 using
为了帮助防止潜在的问题,应避免堆叠,并遵循以下示例模式:
class ExampleProgram { static async Task Main() { var objOne = new ExampleAsyncDisposable(); await using objOne.ConfigureAwait(false); // Interact with the objOne instance. var objTwo = new ExampleAsyncDisposable(); await using objTwo.ConfigureAwait(false)) { // Interact with the objTwo instance. } Console.ReadLine(); } }
StreamReader 对象以读取两个不同文件的内容。
using StreamReader version1 = new StreamReader("file1.txt"), version2 = new StreamReader("file2.txt");