Young87

SmartCat's Blog

So happy to code my life!

游戏开发交流QQ群号60398951

当前位置:首页 >AI写作

c#题例-2025-07-21 18:58:31

当然可以!下面是一道**专家级别**的 C# 程序员逻辑面试题,涵盖了 **委托、事件、闭包、线程安全、异步编程、内存管理**等多个高级概念,适合考察候选人对 C# 语言及其运行机制的深入理解。

---

### 🧠 面试题:事件与闭包的陷阱

**题目:**

请看以下 C# 代码:

```csharp
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

public class EventRaiser
{
public event EventHandler OnEvent = delegate { };

public async Task RaiseEventAsync()
{
await Task.Delay(100); // 模拟异步操作
OnEvent(this, EventArgs.Empty);
}
}

public class TestClass
{
private List _subscriptions = new List();

public void Start()
{
var raiser = new EventRaiser();

EventHandler handler = async (sender, args) =>
{
await Task.Delay(500); // 模拟耗时操作
Console.WriteLine("Event handled.");
};

raiser.OnEvent += handler;

_subscriptions.Add(new DisposableAction(() =>
{
raiser.OnEvent -= handler;
Console.WriteLine("Unsubscribed.");
}));

raiser.RaiseEventAsync();
}

public void Stop()
{
foreach (var d in _subscriptions)
{
d.Dispose();
}
_subscriptions.Clear();
}

private class DisposableAction : IDisposable
{
private readonly Action _action;

public DisposableAction(Action action)
{
_action = action;
}

public void Dispose()
{
_action?.Invoke();
}
}
}
```

然后在主程序中执行如下代码:

```csharp
var test = new TestClass();
test.Start();
test.Stop();
```

---

### ❓问题:

1. 上述代码是否存在**内存泄漏**的风险?为什么?
2. 如果在 `Start()` 中多次调用 `test.Start()`,会发生什么?是否会导致多次订阅?
3. 在 `handler` 中使用 `async void`(即 `EventHandler`)是否安全?可能会引发什么问题?
4. 如何改进这段代码,使其线程安全且避免内存泄漏?

---

### 🧩 考察点:

- **事件订阅的生命周期管理**
- **使用闭包导致对象无法释放的问题**
- **async void 的陷阱与异常处理**
- **IDisposable 的正确使用方式**
- **垃圾回收机制和对象引用链**
- **异步事件处理的正确模式**

---

### ✅ 参考答案(简要):

1. **是的,存在内存泄漏。**
因为 `raiser.OnEvent += handler;` 会强引用 `handler`,而 `handler` 是一个 lambda 表达式,它捕获了 `raiser` 对象(或者至少是 `TestClass` 的某些状态),导致 `raiser` 无法被释放,除非手动取消订阅。

2. **多次调用 `Start()` 会导致重复订阅。**
每次调用 `Start()` 都会创建一个新的 `raiser` 实例并订阅事件,但 `_subscriptions` 列表中只添加了一个 `DisposableAction`,但无法跟踪之前的 `raiser` 实例,因此旧的订阅无法取消,导致多个事件触发。

3. **不安全。**
`EventHandler` 是 `async void`,这意味着异常无法被捕获,且会直接抛到同步上下文中,可能导致程序崩溃。

4. **改进建议:**
- 使用 `async Task` 替代 `async void`,并在事件中使用 `Task.Run` 或其他机制处理异步逻辑。
- 使用弱引用事件模式(如 `WeakEventManager`)来避免内存泄漏。
- 确保每次 `Start()` 都清理之前的订阅。
- 使用 `CancellationToken` 来取消异步操作。

---

如果你需要我提供改进后的代码示例,也可以告诉我 😊

除特别声明,本站所有文章均为原创,如需转载请以超级链接形式注明出处:SmartCat's Blog

上一篇: c#题例-2025-07-22 00:31:46

下一篇: c#题例-2025-07-21 13:25:18

精华推荐