c#题例-2025-06-25 09:38:48
日期: 2025-06-25 分类: AI写作 9次阅读
当然可以!下面是一道**专家级别**的 C# 程序员逻辑面试题,涉及到 **委托、事件、闭包、线程安全和异步编程**等多个高级概念,考察候选人对语言机制的理解深度与实际应用能力。
---
### 🧠 面试题:异步事件订阅中的闭包陷阱
#### 问题描述:
请看以下 C# 控制台程序代码片段。这段代码使用了事件、委托、`async/await` 和循环结构。
```csharp
using System;
using System.Threading.Tasks;
public class EventPublisher
{
public event EventHandler
public async Task PublishDataAsync()
{
await Task.Delay(100); // 模拟异步操作
DataReady?.Invoke(this, "Hello World");
}
}
class Program
{
static async Task Main(string[] args)
{
var publisher = new EventPublisher();
for (int i = 0; i < 5; i++)
{
int localCopy = i;
publisher.DataReady += async (sender, e) =>
{
await Task.Delay(localCopy * 100); // 模拟延迟
Console.WriteLine($"Received data after {localCopy * 100}ms: {e}");
};
}
await publisher.PublishDataAsync();
Console.WriteLine("Published data.");
Console.ReadLine(); // 防止控制台退出
}
}
```
#### 问题:
1. 这段代码输出的结果是什么?为什么?
2. 是否存在潜在的 **内存泄漏或线程安全问题**?如果有,请指出。
3. 如何改进这段代码以避免可能的问题,并确保每个事件处理器按预期执行?
---
### 🔍 参考答案要点:
#### 1. 输出结果分析:
- 所有订阅者会在 `publisher.PublishDataAsync()` 中被触发。
- 每个订阅者的 `localCopy` 是循环变量 `i` 的副本,不会出现常见的“闭包陷阱”(因为用了 `int localCopy = i;`)。
- 所有事件处理函数会并发执行(因为是 `async void`),主函数不会等待它们完成。
- 因此,`"Published data."` 很可能会在所有事件处理之前打印。
- 实际输出顺序不确定,但大致如下:
```
Published data.
Received data after 0ms: Hello World
Received data after 100ms: Hello World
Received data after 200ms: Hello World
Received data after 300ms: Hello World
Received data after 400ms: Hello World
```
但由于是 `async void`,控制台有可能提前关闭(如果没有 `Console.ReadLine()`)。
#### 2. 存在的问题:
- **async void 的风险**:
- 使用 `async void` 作为事件处理函数容易导致异常无法捕获、死锁、控制台提前退出等问题。
- 推荐改为 `async Task` 并显式等待。
- **事件未取消订阅**:
- 如果 `publisher` 生命周期较长,而 `Program` 或其他对象已不再需要接收事件,则会导致内存泄漏。
- 应该使用 `-=` 显式取消订阅,或使用弱引用事件模式。
- **事件执行顺序不可控**:
- 由于是并发执行,不能保证哪个事件先执行完毕。
#### 3. 改进建议:
##### ✅ 改进版本一:将事件改为可等待的任务列表
```csharp
public class EventPublisher
{
public event Func
public async Task PublishDataAsync()
{
await Task.Delay(100);
if (DataReady != null)
{
foreach (var handler in DataReady.GetInvocationList())
{
await ((Func
}
}
}
}
```
然后在 `Main` 方法中:
```csharp
for (int i = 0; i < 5; i++)
{
int localCopy = i;
publisher.DataReady += async () =>
{
await Task.Delay(localCopy * 100);
Console.WriteLine($"Received data after {localCopy * 100}ms");
};
}
await publisher.PublishDataAsync();
Console.WriteLine("All handlers completed.");
```
##### ✅ 改进版本二:使用 `IProgress
---
### 💡 考察点总结:
| 技术点 | 考察内容 |
|--------|----------|
| 委托与事件 | 对多播委托的理解 |
| 异步编程 (`async/await`) | 正确使用异步方法 |
| `async void` vs `async Task` | 对异常处理和生命周期管理的理解 |
| 闭包与捕获变量 | 是否理解值类型与闭包的行为 |
| 内存泄漏 | 是否知道事件可能导致的对象生命周期延长 |
| 线程安全 | 多线程环境下的状态访问是否安全 |
---
如果你想要更多类似级别的题目,我可以继续为你出题 😎
除特别声明,本站所有文章均为原创,如需转载请以超级链接形式注明出处:SmartCat's Blog
标签:AI写作
精华推荐