c#题例-2025-06-27 09:25:31
日期: 2025-06-27 分类: AI写作 10次阅读
当然可以!下面是一道**专家级别**的 C# 逻辑面试题,涉及 **委托、泛型、异步编程、闭包捕获和线程安全**等多个高级概念。这类题目通常用于考察候选人对 C# 语言底层机制的理解以及实际开发中的问题解决能力。
---
### 🧠 面试题:异步委托链与状态捕获
```csharp
using System;
using System.Threading.Tasks;
class Program
{
static async Task Main(string[] args)
{
var tasks = new Task
var results = new int[3];
for (int i = 0; i < 3; i++)
{
int counter = i;
Func
{
await Task.Delay(100);
return ++counter;
};
tasks[i] = func();
tasks[i].ContinueWith(t => results[i] = t.Result);
}
await Task.WhenAll(tasks);
Console.WriteLine(string.Join(", ", results));
}
}
```
---
### ❓问题:
**当运行上述代码时,控制台输出的结果是什么?请解释每一步发生了什么,并分析是否存在潜在的问题(例如线程安全、变量捕获等)。**
---
### ✅参考答案要点:
#### 🔹 输出结果:
```
2, 2, 2
```
> 注意:在某些特殊情况下,可能不是完全一致,但大多数情况下是 `2, 2, 2`。
#### 🔹 关键分析点:
1. **变量捕获陷阱(Closure Capturing)**
- 原始写法中,如果使用的是 `i` 而不是 `counter`,会出现经典的“循环变量被捕获”问题。
- 在本题中,`int counter = i;` 是关键语句,它创建了一个局部副本,避免了这个问题。
2. **异步委托执行顺序**
- `tasks[i] = func();` 是立即调用了委托并启动任务。
- `ContinueWith` 是在任务完成后赋值给 `results[i]`,但由于所有任务几乎同时完成,且 `i` 已经递增完毕,这里存在一个**新的变量捕获问题**!
3. **ContinueWith 中的索引问题**
- 在 `ContinueWith(t => results[i] = t.Result)` 中,lambda 表达式捕获的是变量 `i`,而不是其当前值。
- 所有 `ContinueWith` 的 lambda 实际上都引用了同一个 `i` 变量,最终 `i == 3`,导致越界访问或错误索引。
4. **正确做法:为每个 ContinueWith 创建局部副本**
```csharp
int localIndex = i;
tasks[i].ContinueWith(t => results[localIndex] = t.Result);
```
5. **线程安全**
- `results[localIndex] = t.Result` 是线程安全的,因为每个 `localIndex` 是独立的,不会有多个线程同时写入同一位置。
---
### 💡扩展思考:
- 如果将 `++counter` 改成 `Interlocked.Increment(ref counter)` 是否有必要?
- 如果用 `Parallel.For` 或 `Task.Run` 替代当前结构会有什么不同?
---
### 🧩总结:
这道题不仅测试了对 C# 异步编程模型的理解,还深入考察了:
- 委托与 Lambda 表达式的变量捕获机制;
- 异步任务调度和 Continuation 的行为;
- 线程安全与共享状态管理;
- 对常见陷阱(如循环变量捕获)的规避能力。
---
如果你需要更多类似难度的题目,我可以继续提供,涵盖 LINQ优化、表达式树、内存泄漏排查、不安全代码、自定义 awaiter 等高级主题。
除特别声明,本站所有文章均为原创,如需转载请以超级链接形式注明出处:SmartCat's Blog
标签:AI写作
精华推荐