c#题例-2025-07-13 07:58:37
日期: 2025-07-13 分类: AI写作 12次阅读
当然可以!下面是一道**专家级别**的 C# 程序员逻辑面试题,涉及 **闭包、委托、异步编程、捕获变量和线程安全**等多个高级知识点:
---
### 🧠 面试题:闭包陷阱与异步执行顺序
```csharp
using System;
using System.Threading.Tasks;
class Program
{
static async Task Main(string[] args)
{
var tasks = new Task[10];
for (var i = 0; i < 10; i++)
{
tasks[i] = Task.Run(async () =>
{
await Task.Delay(100); // 模拟异步操作
Console.WriteLine(i);
});
}
await Task.WhenAll(tasks);
}
}
```
---
### ❓问题:
这段代码输出的结果是什么?为什么会这样?如何修改才能让输出是 `0` 到 `9` 的顺序?
---
### 💡考查点:
1. **变量捕获陷阱(Closure over loop variable)**
- 在 C# 中,`for` 循环中的 `i` 是被多个委托共享的,而不是每次迭代都创建一个新的副本。
- 因此,所有任务最终可能打印的是 `i` 最终的值(即 10),而不是各自迭代时的当前值。
2. **异步执行顺序**
- 所有任务都在异步等待,因此主线程不会立即执行 `Console.WriteLine(i)`,而是继续循环直到 `i == 10`。
3. **多线程访问共享变量的线程安全问题**
- 多个线程访问同一个变量 `i`,而它在循环结束后已经被改变。
4. **解决方案技巧**
- 如何正确地“固定”当前迭代的值,以供每个任务使用。
---
### ✅参考答案:
#### 输出结果:
```
10
10
10
...
10
```
(共输出十次 10)
#### 原因:
因为 `i` 是一个**循环变量**,它在整个循环中是同一个变量。所有的 lambda 表达式都引用了这个变量,而不是它的某个快照。当异步方法真正执行到 `Console.WriteLine(i)` 时,循环早已完成,此时 `i == 10`。
#### 正确写法(推荐方式):
```csharp
for (var i = 0; i < 10; i++)
{
int local = i; // 创建局部副本,确保每次迭代都有新的变量
tasks[i] = Task.Run(async () =>
{
await Task.Delay(100);
Console.WriteLine(local);
});
}
```
或者(不推荐,但也能工作):
```csharp
for (int i = 0; i < 10; i++)
{
int capture = i;
tasks[i] = Task.Run(async () =>
{
await Task.Delay(100);
Console.WriteLine(capture);
});
}
```
> ⚠️ 注意:从 C# 5 开始,在 `foreach` 循环中这个问题已被修复,但在 `for` 循环中仍然存在。
---
### 🧩拓展追问(进阶):
- 如果你将 `Task.Run` 替换为 `Task.Factory.StartNew(() => ..., TaskCreationOptions.LongRunning)`,是否会影响行为?
- 如果你想保证输出顺序是 `0 ~ 9` 并且按顺序打印,应如何处理?
- 如果你在 `Parallel.For` 中进行类似操作,该如何避免闭包陷阱?
---
如果你需要更多这类题目(比如 LINQ 表达式树、泛型约束、反射 emit、GC 根分析等),我也可以继续出。
除特别声明,本站所有文章均为原创,如需转载请以超级链接形式注明出处:SmartCat's Blog
标签:AI写作
精华推荐