为何含yield的IEnumerable方法异常延迟抛出?如何取消该行为?
C#中带yield的方法延迟抛出异常的原因及解决方法
问题
以下C#代码中的异常仅在枚举返回值时才抛出,这是为什么?如何让编译器取消该延迟执行行为?
public IEnumerable<string> BuildFor(Environment environment) { if (environment == Environment.None) { throw new Exception($"Impossible to build for environment '{environment}'."); } string clientId = ClientIds.Api[environment]; foreach (string scope in scopes) { yield return $"api://{clientId}/{scope}"; } }
原因
这是C#编译器对包含yield return的方法的特有处理:
- 编译器会将这类方法转换为状态机实现,当你调用
BuildFor时,并不会立即执行方法内部的代码,仅返回一个实现了IEnumerable<string>的状态机对象。 - 只有当你开始枚举这个返回对象(比如通过
foreach遍历、调用ToList()/ToArray()等操作)时,状态机才会启动并执行方法内的逻辑,此时参数校验的异常才会被抛出。
解决方法
要取消延迟执行,只需移除yield return,改为立即执行所有逻辑并返回已完成的集合,示例代码如下:
方式1:手动构建集合
public IEnumerable<string> BuildFor(Environment environment) { if (environment == Environment.None) { throw new Exception($"Impossible to build for environment '{environment}'."); } string clientId = ClientIds.Api[environment]; var resultList = new List<string>(); foreach (string scope in scopes) { resultList.Add($"api://{clientId}/{scope}"); } return resultList; }
方式2:使用LINQ简化
public IEnumerable<string> BuildFor(Environment environment) { if (environment == Environment.None) { throw new Exception($"Impossible to build for environment '{environment}'."); } string clientId = ClientIds.Api[environment]; return scopes.Select(scope => $"api://{clientId}/{scope}").ToList(); }
修改后,调用BuildFor方法时会立即执行参数校验和集合构建,异常会在方法调用阶段就抛出,而非等到枚举操作时。
内容的提问来源于stack exchange,提问作者José Ramírez
相关产品推荐
相关产品推荐

