c#题例-2025-07-18 21:52:20
日期: 2025-07-18 分类: AI写作 13次阅读
当然可以!下面是一道**专家级别**的 C# 逻辑面试题,它不仅考察对 C# 语言特性的掌握,还涉及**委托、事件、闭包、线程安全、异步编程模型**等多个高级概念的综合运用。
---
### 🧠 面试题:闭包陷阱与事件生命周期管理
#### 📌 题目描述:
你正在开发一个多线程的 C# 应用程序,其中有一个 `Worker` 类,它会周期性地触发事件,并允许订阅者通过 lambda 表达式注册回调。
请看以下代码片段:
```csharp
public class Worker
{
public event EventHandler DoWork;
public void Start()
{
Task.Run(async () =>
{
while (true)
{
await Task.Delay(1000);
DoWork?.Invoke(this, EventArgs.Empty);
}
});
}
}
public class Program
{
public static void Main()
{
var worker = new Worker();
worker.Start();
int count = 0;
worker.DoWork += (sender, e) =>
{
count++;
Console.WriteLine($"Work count: {count}");
};
Console.ReadLine();
}
}
```
#### ❓问题:
1. **这段代码存在什么潜在问题?**
2. **如果在 `Main` 方法中添加如下代码,会发生什么?为什么会发生?**
```csharp
worker = null;
GC.Collect();
```
3. **如何修改 `Worker` 类或其使用方式,以避免上述问题?请写出修改后的代码并解释其原理。**
---
### 💡 考察点解析:
| 考察点 | 说明 |
|--------|------|
| **闭包与捕获变量** | Lambda 表达式捕获了 `count` 变量,形成闭包,可能导致意外行为或内存泄漏。 |
| **事件与内存泄漏** | 事件订阅者未取消订阅,即使外部变量置为 null,仍可能被根引用保留。 |
| **线程与异步安全** | 使用 `Task.Run` 和 `await Task.Delay` 模拟定时器,需考虑线程安全与资源释放。 |
| **GC 与对象生命周期** | 即使显式设置对象为 null 并调用 `GC.Collect()`,由于事件订阅的存在,对象仍无法被回收。 |
| **弱引用或事件解耦机制** | 是否了解 `WeakReference`、`EventHandlerList`、`IWeakEventPattern` 等高级机制。 |
---
### ✅ 参考答案(节选):
#### 1. **潜在问题:**
- **内存泄漏**:lambda 表达式捕获了局部变量 `count`,而 `Worker` 对象持有该事件的引用,导致 `Worker` 实例和 `Main` 方法上下文无法被回收。
- **闭包陷阱**:多个 lambda 可能共享同一个 `count` 变量副本,导致计数错误。
- **未取消订阅**:事件订阅者未主动取消订阅,造成资源无法释放。
#### 2. **添加 `worker = null; GC.Collect();` 后:**
不会触发 `Worker` 实例的回收,因为:
- `Worker` 实例仍然被它自己的 `DoWork` 事件所引用(通过 lambda)。
- 即使外部引用置为 null,GC 也无法回收,因为事件链上仍有引用路径。
#### 3. **修改方案:**
使用**弱引用事件模式**或手动取消订阅。以下是使用取消订阅的简单修改:
```csharp
public class Program
{
public static void Main()
{
var worker = new Worker();
worker.Start();
int count = 0;
EventHandler handler = (sender, e) =>
{
count++;
Console.WriteLine($"Work count: {count}");
};
worker.DoWork += handler;
// 模拟一段时间后取消订阅
Task.Delay(5000).ContinueWith(_ =>
{
worker.DoWork -= handler;
Console.WriteLine("Unsubscribed.");
});
Console.ReadLine();
}
}
```
---
### 🧪 进阶思考(面试官可追问):
- 如何实现一个**支持弱引用事件订阅的 Worker 类**?
- 如何使用 `IWeakEventListener` 接口(WPF)避免内存泄漏?
- 如何使用 `ConditionalWeakTable` 实现自定义弱引用事件机制?
---
如果你希望我继续提供类似难度的题目(如并发、反射、表达式树、性能优化等方向),欢迎继续提问!
除特别声明,本站所有文章均为原创,如需转载请以超级链接形式注明出处:SmartCat's Blog
标签:AI写作
上一篇:无
精华推荐