c#题例-2025-07-21 18:58:31
日期: 2025-07-21 分类: AI写作 101次阅读
当然可以!下面是一道**专家级别**的 C# 程序员逻辑面试题,涵盖了 **委托、事件、闭包、线程安全、异步编程、内存管理**等多个高级概念,适合考察候选人对 C# 语言及其运行机制的深入理解。
---
### 🧠 面试题:事件与闭包的陷阱
**题目:**
请看以下 C# 代码:
```csharp
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
public class EventRaiser
{
    public event EventHandler
    public async Task RaiseEventAsync()
    {
        await Task.Delay(100); // 模拟异步操作
        OnEvent(this, EventArgs.Empty);
    }
}
public class TestClass
{
    private List
    public void Start()
    {
        var raiser = new EventRaiser();
        EventHandler
        {
            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
标签:AI写作
精华推荐

