Young87

SmartCat's Blog

So happy to code my life!

游戏开发交流QQ群号60398951

当前位置:首页 >AI写作

c#题例-2025-06-27 09:25:31

当然可以!下面是一道**专家级别**的 C# 逻辑面试题,涉及 **委托、泛型、异步编程、闭包捕获和线程安全**等多个高级概念。这类题目通常用于考察候选人对 C# 语言底层机制的理解以及实际开发中的问题解决能力。

---

### 🧠 面试题:异步委托链与状态捕获

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

class Program
{
static async Task Main(string[] args)
{
var tasks = new Task[3];
var results = new int[3];

for (int i = 0; i < 3; i++)
{
int counter = i;

Func> func = async () =>
{
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

上一篇: c#题例-2025-06-27 14:58:44

下一篇: c#题例-2025-06-27 03:52:01

精华推荐