c#题例-2025-06-26 09:58:44
日期: 2025-06-26 分类: AI写作 7次阅读
当然可以!下面是一道**专家级别**的 C# 程序员逻辑面试题,涉及 **闭包、委托、异步编程和捕获变量** 的深入理解,是考察候选人对 C# 语言机制和底层行为掌握程度的好题目。
---
### 🧠 面试题:你了解 `async/await` 和 `foreach` 中的变量捕获吗?
请看以下代码:
```csharp
using System;
using System.Threading.Tasks;
using System.Collections.Generic;
class Program
{
static async Task Main(string[] args)
{
var tasks = new List
foreach (var i in new[] { 1, 2, 3 })
{
var delay = i * 1000;
Task task = Task.Run(async () =>
{
await Task.Delay(delay);
Console.WriteLine($"Value: {i}, Delay: {delay}");
});
tasks.Add(task);
}
await Task.WhenAll(tasks);
}
}
```
#### 问题:
- 这段程序运行后输出的结果会是什么?为什么?
- 如果将 `foreach (var i in ...)` 改为 `for (int i = 0; i < 3; i++)`,结果又会发生什么变化?解释其背后原理。
---
### ✅ 参考答案与解析:
#### 第一部分输出(使用 `foreach`):
可能输出如下(顺序不确定,因为是并发执行):
```
Value: 1, Delay: 1000
Value: 2, Delay: 2000
Value: 3, Delay: 3000
```
✅ 正确原因:
在 `foreach` 循环中,每次迭代都会创建一个新的循环变量 `i`(从 C# 5 开始),因此在 lambda 表达式中捕获的是当前迭代的值。所以每个任务都正确地捕获了对应的 `i` 值。
#### 第二部分修改为 `for` 后的变化:
```csharp
for (int i = 0; i < 3; i++)
{
int delay = i * 1000;
Task task = Task.Run(async () =>
{
await Task.Delay(delay);
Console.WriteLine($"Value: {i}, Delay: {delay}");
});
tasks.Add(task);
}
```
此时输出可能是:
```
Value: 3, Delay: 0
Value: 3, Delay: 1000
Value: 3, Delay: 2000
```
❌ 错误原因:
这是经典的 **变量捕获陷阱(captured variable trap)**。
- 在 `for` 循环中,变量 `i` 是在整个循环中复用的。
- 所有 lambda 表达式捕获的是同一个变量 `i`,而不是它的某个特定迭代时的值。
- 当任务真正执行的时候,`i` 已经变成 3 了(循环结束条件),所以所有任务中的 `i` 都是 3。
#### 如何修复?
只需在 `for` 循环内部引入一个局部变量即可:
```csharp
for (int i = 0; i < 3; i++)
{
int capturedI = i;
int delay = capturedI * 1000;
Task task = Task.Run(async () =>
{
await Task.Delay(delay);
Console.WriteLine($"Value: {capturedI}, Delay: {delay}");
});
tasks.Add(task);
}
```
这样就能确保每个 lambda 捕获的是正确的值。
---
### 🔍 考察点总结:
- 对 `foreach` 和 `for` 循环变量作用域的理解
- 掌握闭包(Closure)和变量捕获的行为差异
- 异步任务调度与延迟执行的时机
- 多线程环境中共享变量的风险
- 解决变量捕获陷阱的最佳实践
---
如果你需要更多类似难度级别的 C# 或 .NET 高级面试题,我可以继续为你出题 😊
除特别声明,本站所有文章均为原创,如需转载请以超级链接形式注明出处:SmartCat's Blog
标签:AI写作
精华推荐