什么是析构函数

析构函数(Destructor),在 C#中也称为终结器(Finalizer),是一种特殊的方法,用于在对象被垃圾回收器回收之前执行清理操作。析构函数为托管代码提供了一种在对象生命周期结束时执行必要清理工作的机制。

析构函数的语法特征

1
2
3
4
5
6
7
8
9
public class MyClass
{
// 析构函数语法:使用 ~ 符号 + 类名
~MyClass()
{
// 清理代码
Console.WriteLine("MyClass 对象正在被销毁");
}
}

语法要点:

  • 析构函数名必须与类名相同
  • 前面加上波浪号(~)
  • 不能有访问修饰符、参数或返回值
  • 每个类只能有一个析构函数
  • 不能被直接调用,只能由垃圾回收器调用

析构函数的工作原理

编译器转换机制

当编译器遇到析构函数时,会自动将其转换为重写的 Finalize 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 源代码
public class ResourceManager
{
~ResourceManager()
{
// 清理资源
ReleaseResources();
}

private void ReleaseResources()
{
Console.WriteLine("释放非托管资源");
}
}

// 编译器实际生成的代码(简化版)
public class ResourceManager
{
protected override void Finalize()
{
try
{
// 清理资源
ReleaseResources();
}
finally
{
base.Finalize(); // 调用父类的Finalize
}
}
}

垃圾回收与终结队列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public class FinalizationDemo
{
private static int _instanceCount = 0;
private readonly int _instanceId;

public FinalizationDemo()
{
_instanceId = ++_instanceCount;
Console.WriteLine($"对象 {_instanceId} 已创建");
}

~FinalizationDemo()
{
Console.WriteLine($"对象 {_instanceId} 正在终结");

// 模拟清理工作
Thread.Sleep(100);

Console.WriteLine($"对象 {_instanceId} 终结完成");
}
}

// 演示垃圾回收过程
public class GCDemo
{
public static void DemonstrateFinalization()
{
Console.WriteLine("=== 垃圾回收与终结演示 ===");

// 创建多个对象
for (int i = 0; i < 5; i++)
{
var obj = new FinalizationDemo();
// 对象离开作用域,成为垃圾回收候选
}

Console.WriteLine("触发垃圾回收...");
GC.Collect(); // 强制垃圾回收
GC.WaitForPendingFinalizers(); // 等待终结器执行

Console.WriteLine("垃圾回收完成");
}
}

析构函数的作用与应用场景

非托管资源清理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
public class FileManager : IDisposable
{
private IntPtr _fileHandle; // 非托管文件句柄
private bool _disposed = false;

public FileManager(string filePath)
{
// 假设这里打开了一个文件句柄
_fileHandle = OpenFile(filePath);
Console.WriteLine($"文件句柄已打开: {_fileHandle}");
}

~FileManager()
{
Console.WriteLine("析构函数被调用");
Dispose(false); // 清理非托管资源
}

public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this); // 阻止析构函数调用
}

protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
// 清理托管资源
Console.WriteLine("清理托管资源");
}

// 清理非托管资源
if (_fileHandle != IntPtr.Zero)
{
CloseFile(_fileHandle);
Console.WriteLine($"文件句柄已关闭: {_fileHandle}");
_fileHandle = IntPtr.Zero;
}

_disposed = true;
}
}

// 模拟P/Invoke调用
private IntPtr OpenFile(string path) => new IntPtr(Random.Shared.Next(1000, 9999));
private void CloseFile(IntPtr handle) { /* 关闭文件 */ }
}

事件订阅清理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class EventPublisher
{
public event Action<string> MessageReceived;

public void PublishMessage(string message)
{
MessageReceived?.Invoke(message);
}
}

public class EventSubscriber
{
private readonly EventPublisher _publisher;
private readonly string _name;

public EventSubscriber(EventPublisher publisher, string name)
{
_publisher = publisher;
_name = name;

// 订阅事件
_publisher.MessageReceived += OnMessageReceived;
Console.WriteLine($"订阅者 {_name} 已注册");
}

~EventSubscriber()
{
// 在析构函数中取消事件订阅,防止内存泄漏
if (_publisher != null)
{
_publisher.MessageReceived -= OnMessageReceived;
Console.WriteLine($"订阅者 {_name} 已取消订阅");
}
}

private void OnMessageReceived(string message)
{
Console.WriteLine($"[{_name}] 收到消息: {message}");
}
}

调试和监控

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public class ResourceTracker
{
private static readonly List<WeakReference> _trackedObjects = new();
private static readonly object _lock = new object();

private readonly string _objectType;
private readonly DateTime _createdTime;

public ResourceTracker(string objectType)
{
_objectType = objectType;
_createdTime = DateTime.Now;

lock (_lock)
{
_trackedObjects.Add(new WeakReference(this));
}

Console.WriteLine($"[跟踪] {_objectType} 对象已创建 - {_createdTime:HH:mm:ss.fff}");
}

~ResourceTracker()
{
var lifetime = DateTime.Now - _createdTime;
Console.WriteLine($"[跟踪] {_objectType} 对象已销毁 - 生存时间: {lifetime.TotalMilliseconds:F0}ms");

// 可以在这里记录到日志文件或监控系统
LogObjectDestruction(_objectType, lifetime);
}

private void LogObjectDestruction(string type, TimeSpan lifetime)
{
// 实际应用中可以写入日志文件或发送到监控系统
// Logger.Info($"Object {type} destroyed after {lifetime.TotalMilliseconds}ms");
}

public static void PrintAliveObjects()
{
lock (_lock)
{
var aliveCount = _trackedObjects.Count(wr => wr.IsAlive);
Console.WriteLine($"当前存活的跟踪对象数量: {aliveCount}");
}
}
}

高级应用:FinalizerHooker 实现

让我们深入分析您提供的 FinalizerHooker 代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
using System.Runtime.CompilerServices;

/// <summary>
/// 终结器钩子 - 允许为任意对象附加析构回调
/// </summary>
public static class FinalizerHooker<T>
where T : class
{
// 使用ConditionalWeakTable确保不会阻止对象被回收
private static readonly ConditionalWeakTable<T, Finalizer> _weak =
new ConditionalWeakTable<T, Finalizer>();

/// <summary>
/// 为指定实例添加析构回调
/// </summary>
/// <param name="instance">目标实例</param>
/// <param name="action">析构时执行的回调</param>
public static void Hook(T instance, Action<T> action)
{
if (_weak.TryGetValue(instance, out var existedHookAction))
{
// 如果已存在Hook,则追加新的Action
existedHookAction.HookAction += action;
}
else
{
// 创建新的Finalizer并关联到实例
_weak.Add(instance, new Finalizer(instance, action));
}
}

/// <summary>
/// 移除指定实例的析构回调
/// </summary>
public static bool UnHook(T instance)
{
return _weak.Remove(instance);
}

/// <summary>
/// 内部终结器类
/// </summary>
private class Finalizer
{
public Finalizer(T instance, Action<T> hookAction)
{
Instance = instance;
HookAction = hookAction;
}

~Finalizer()
{
try
{
// 在析构时调用Hook的Action
HookAction?.Invoke(Instance);
}
catch (Exception ex)
{
// 析构函数中的异常不应该传播
Console.WriteLine($"FinalizerHooker 执行异常: {ex.Message}");
}
}

public T Instance { get; }
public Action<T> HookAction { get; set; }
}
}

// 使用示例
public class FinalizerHookerDemo
{
public static void DemonstrateFinalizerHooker()
{
Console.WriteLine("=== FinalizerHooker 演示 ===");

var obj1 = new TestObject("Object1");
var obj2 = new TestObject("Object2");

// 为对象添加析构回调
FinalizerHooker<TestObject>.Hook(obj1, instance =>
{
Console.WriteLine($"Hook回调: {instance.Name} 正在被销毁");
});

FinalizerHooker<TestObject>.Hook(obj2, instance =>
{
Console.WriteLine($"Hook回调: {instance.Name} 的资源需要清理");
// 可以在这里执行复杂的清理逻辑
});

// 添加多个回调到同一个对象
FinalizerHooker<TestObject>.Hook(obj1, instance =>
{
Console.WriteLine($"第二个Hook: {instance.Name} 执行额外清理");
});

Console.WriteLine("对象创建完成,准备销毁...");

// 清除引用
obj1 = null;
obj2 = null;

// 触发垃圾回收
GC.Collect();
GC.WaitForPendingFinalizers();

Console.WriteLine("FinalizerHooker 演示完成");
}
}

public class TestObject
{
public string Name { get; }

public TestObject(string name)
{
Name = name;
Console.WriteLine($"TestObject {Name} 已创建");
}
}

重要注意事项

性能影响

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public class PerformanceComparison
{
public static void ComparePerformance()
{
const int iterations = 1000000;

// 测试没有析构函数的类
var sw1 = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++)
{
var obj = new ClassWithoutFinalizer();
}
sw1.Stop();

// 测试有析构函数的类
var sw2 = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++)
{
var obj = new ClassWithFinalizer();
}
sw2.Stop();

Console.WriteLine($"无析构函数类创建时间: {sw1.ElapsedMilliseconds}ms");
Console.WriteLine($"有析构函数类创建时间: {sw2.ElapsedMilliseconds}ms");
Console.WriteLine($"性能差异: {((double)sw2.ElapsedMilliseconds / sw1.ElapsedMilliseconds - 1) * 100:F1}%");
}
}

public class ClassWithoutFinalizer
{
public int Value { get; set; }
}

public class ClassWithFinalizer
{
public int Value { get; set; }

~ClassWithFinalizer()
{
// 即使是空的析构函数也会影响性能
}
}

异常处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class SafeFinalizerExample
{
private bool _disposed = false;

~SafeFinalizerExample()
{
try
{
// 析构函数中必须进行异常处理
RiskyCleanupOperation();
}
catch (Exception ex)
{
// 记录异常但不重新抛出
// 在析构函数中抛出异常会导致应用程序崩溃
LogException("析构函数异常", ex);
}
}

private void RiskyCleanupOperation()
{
// 可能抛出异常的清理操作
if (Random.Shared.Next(10) == 0)
{
throw new InvalidOperationException("清理操作失败");
}

Console.WriteLine("清理操作成功");
}

private void LogException(string context, Exception ex)
{
Console.WriteLine($"[ERROR] {context}: {ex.Message}");
// 实际应用中应该写入日志文件
}
}

与 IDisposable 的协作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public class ProperResourceManagement : IDisposable
{
private FileStream _fileStream;
private bool _disposed = false;

public ProperResourceManagement(string filePath)
{
_fileStream = new FileStream(filePath, FileMode.OpenOrCreate);
}

// 实现IDisposable模式
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this); // 重要:阻止析构函数调用
}

protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
// 清理托管资源
_fileStream?.Dispose();
Console.WriteLine("托管资源已清理");
}

// 清理非托管资源(如果有的话)
Console.WriteLine("非托管资源已清理");

_disposed = true;
}
}

// 析构函数作为备用清理机制
~ProperResourceManagement()
{
Console.WriteLine("警告: 对象未正确Dispose,使用析构函数清理");
Dispose(false);
}
}

最佳实践建议

推荐做法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class BestPracticeExample : IDisposable
{
private bool _disposed = false;

// 1. 总是实现IDisposable模式
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
// 清理托管资源
}
// 清理非托管资源
_disposed = true;
}
}

// 2. 析构函数应该简单可靠
~BestPracticeExample()
{
Dispose(false);
}

// 3. 提供检查是否已释放的方法
protected void ThrowIfDisposed()
{
if (_disposed)
{
throw new ObjectDisposedException(GetType().Name);
}
}
}

避免的做法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class BadPracticeExample
{
// 不要在析构函数中访问其他托管对象
~BadPracticeExample()
{
// 错误:其他对象可能已经被回收
// SomeOtherManagedObject.DoSomething();
}

// 不要在析构函数中抛出异常
~BadPracticeExample()
{
// 错误:会导致应用程序崩溃
// throw new Exception("析构失败");
}

// 不要在析构函数中执行耗时操作
~BadPracticeExample()
{
// 错误:会阻塞终结器线程
// Thread.Sleep(5000);
}
}

实际应用示例

数据库连接管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class DatabaseConnection : IDisposable
{
private IDbConnection _connection;
private bool _disposed = false;

public DatabaseConnection(string connectionString)
{
_connection = new SqlConnection(connectionString);
_connection.Open();
Console.WriteLine("数据库连接已打开");
}

~DatabaseConnection()
{
Console.WriteLine("警告: 数据库连接未正确释放,使用析构函数清理");
Dispose(false);
}

public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing && _connection != null)
{
if (_connection.State == ConnectionState.Open)
{
_connection.Close();
Console.WriteLine("数据库连接已正常关闭");
}
_connection.Dispose();
}
_disposed = true;
}
}
}

总结

析构函数是 C#提供的重要资源管理机制,主要用于:

  1. 非托管资源清理 - 确保系统资源得到释放
  2. 备用清理机制 - 当开发者忘记调用 Dispose 时的保险措施
  3. 调试和监控 - 跟踪对象生命周期和资源使用情况

关键要点:

  • 析构函数会影响 GC 性能,谨慎使用
  • 总是配合 IDisposable 模式使用
  • 使用 GC.SuppressFinalize 优化性能