c#题例-2025-07-22 18:25:19
日期: 2025-07-22 分类: AI写作 13次阅读
当然可以!下面是一道**专家级别**的 C# 程序员逻辑面试题,涉及 **闭包、异步编程、变量捕获机制** 和 **多线程环境下的陷阱**,非常适合考察候选人对 C# 高级特性的理解和实际开发经验。
---
### 🧠 面试题:异步闭包陷阱(Asynchronous Closure Gotcha)
#### 问题描述:
请看以下 C# 代码片段,并回答控制台输出的内容是什么?并解释原因。
```csharp
using System;
using System.Threading.Tasks;
class Program
{
static async Task Main(string[] args)
{
for (var i = 0; i < 5; i++)
{
int counter = i;
Task.Run(async () =>
{
await Task.Delay(100); // 模拟异步操作
Console.WriteLine($"Value: {counter}");
});
}
Console.WriteLine("All tasks started.");
await Task.Delay(1000); // 等待所有任务完成
}
}
```
#### 问题进阶:
1. 控制台输出是什么?为什么?
2. 如果将 `int counter = i;` 改为 `var counter = i;`,结果会变化吗?
3. 如果去掉 `counter` 变量,直接在 lambda 中使用 `i`,结果会怎样?
4. 如何确保输出顺序是确定的(即 0 到 4)?
---
### ✅ 答案与解析:
#### 1. 输出内容:
```
All tasks started.
Value: 0
Value: 1
Value: 2
Value: 3
Value: 4
```
**原因:**
- 在 C# 中,`for` 循环变量 `i` 是在循环外部声明的(在 C# 5 之前是这样),但在 C# 5 及以后版本中,每次迭代都会创建一个新的变量实例。
- 这里我们使用了 `int counter = i;`,这实际上创建了一个新的变量副本,用于每个 lambda 表达式中。
- 每个 `Task.Run` 内部捕获的是该次迭代的 `counter` 变量,而不是 `i`,因此每个任务输出的是正确的值。
#### 2. `var counter = i;` 是否影响结果?
**不影响。**
- 因为 `var` 在这里是隐式类型推断,实际类型仍然是 `int`,所以行为一致。
- 关键点是变量捕获的时机和副本的创建。
#### 3. 如果使用 `i` 而不是 `counter`:
```csharp
Task.Run(async () =>
{
await Task.Delay(100);
Console.WriteLine($"Value: {i}");
});
```
**输出可能为:**
```
All tasks started.
Value: 5
Value: 5
Value: 5
Value: 5
Value: 5
```
**原因:**
- 所有 lambda 表达式捕获的是同一个变量 `i`,而不是每次迭代的副本。
- 当异步任务真正执行时,循环已经完成,`i` 的值已经是 5。
- 这是经典的 **闭包捕获变量陷阱(Closure Captured Variable Gotcha)**。
#### 4. 如何确保输出顺序是 0 到 4?
使用 `counter` 是一种解决方式。另一种方式是使用 `foreach`:
```csharp
foreach (var item in new int[] { 0, 1, 2, 3, 4 })
{
Task.Run(async () =>
{
await Task.Delay(100);
Console.WriteLine($"Value: {item}");
});
}
```
- 因为 `foreach` 循环中每次迭代都会创建一个新的变量副本,避免了闭包陷阱。
---
### 🧪 考察点总结:
- 对 C# 中变量捕获机制的理解
- 对闭包和 Lambda 表达式的掌握
- 异步编程中任务调度和变量生命周期
- 多线程环境下的常见陷阱
- 对 C# 语言版本差异的了解(如 C# 5 中的 `for` 变量改进)
---
如果你需要我再出一道涉及设计模式、LINQ、表达式树、或者 AOP 相关的专家级题目,也可以告诉我 😊
除特别声明,本站所有文章均为原创,如需转载请以超级链接形式注明出处:SmartCat's Blog
标签:AI写作
精华推荐