C# yield

本文通过一个具体的示例,详细介绍了C#中的yield关键字如何工作,特别是它在可迭代方法中的作用方式。文章解释了yield return在不同场景下的行为差异,并展示了如何正确使用以避免常见陷阱。

  C#中的yield可以应用在一个可迭代的方法中,我们必须真正理解此关键词,才能将它正确的应用到实际生产中。为了说明yield会出现让我们迷惑的结果,下面先定义一个MyObject类:

1     class MyObject
2     {
3        public int Value{get;set;}
4     }

  MyObject类用于存放一个值,下面定义一个IEnumerable<MyObject>的方法:

1 IEnumerable<MyObject> GetYieldObjects()
2 {
3     for (int i = 0; i < 10; i++)
4     {
5         Console.WriteLine("GetObjects() i="+i.ToString());
6         yield return new MyObject() { Value = i };
7     }
8 }

   方法中用yield return返回实例化的MyObject对象,为了便于记录GetYieldObjects()方法实际调用的过程,这里用Console.WriteLine来记录每次调用的过程。下面声明一个objects对象,然后进行第一次迭代,在第一次迭代过程中,对每个迭代项目的值进行+10处理,然后进行第2此迭代,看看是否会将处理后的值进行输出:

 1 //此时不调用GetObjects()的内部循环
 2 var objects = GetYieldObjects();
 3 //每次迭代会调用GetObjects()对应i的内容
 4 foreach (var item in objects)
 5 {
 6     Console.WriteLine("1 foreach (var item in objects) item.Value=" + item.Value.ToString());//0..9
 7     item.Value += 10;
 8 }
 9 //每次迭代会调用GetObjects()对应i的内容
10 foreach (var item in objects)
11 {
12     Console.WriteLine("2 foreach (var item in objects) item.Value=" + item.Value.ToString());//0..9
13     Console.WriteLine(item.Value.ToString());//0..9
14 }

   在第2次迭代时,输出的还是0到9,并不是10到19.请看下面的迭代过程:

当第一次迭代时候,每次迭代会调用GetObject()方法中的yield return语句,而不是在var objects=GetObjects()进行实例化。当yield取到一个MyObject对象后立刻返回给迭代器,并将其值进行输出,然后继续调用GetObjects方法中的yield return语句,

因此每次循环时,实际上都是一个新的对象。如下图所示:

 

   当然如果将上面的代码稍作修改,用objects=GetYieldObjects().ToArray()的话,那么第二次输出的就是10到19:

 1 objects = GetYieldObjects().ToArray();
 2 foreach (var item in objects)
 3 {
 4     item.Value += 10;
 5 }
 6 
 7 foreach (var item in objects)
 8 {
 9     Console.WriteLine(item.Value.ToString());//10..19
10 }

 

转载于:https://www.cnblogs.com/isaboy/p/c_yield.html

03-22
### C# 中 `yield` 的用法 在 C# 编程语言中,`yield` 关键字用于定义迭代器方法或匿名函数中的每次返回操作。它允许开发者创建一种特殊的枚举模式,在不占用大量内存的情况下逐步生成序列项[^4]。 #### 基本语法 当在一个方法中使用 `yield return` 语句时,该方法会变成一个迭代器。调用此方法不会立即执行其主体逻辑;相反,编译器会在后台将其转换为实现 `IEnumerable<T>` 或 `IEnumerator<T>` 接口的对象。以下是基本的语法结构: ```csharp public IEnumerable<int> GetNumbers() { yield return 1; yield return 2; yield return 3; } ``` 上述代码片段展示了如何通过多次调用 `yield return` 来逐一提供数值给调用方。每当调用者请求下一个元素时,控制权都会回到迭代器内部并继续执行直到遇到下一次 `yield return` 或到达方法结束位置[^4]。 #### 使用场景 - **延迟计算**:只有在实际需要某个值的时候才会去求解这个值。 - **节省资源**:对于大数据集合或者无限流数据源来说尤为重要,因为它不需要一次性加载整个列表到内存里就可以逐条处理记录。 下面是一个更复杂的例子展示如何利用文件读取功能来构建字符串数组而无需将全部内容载入RAM: ```csharp public static IEnumerable<string> ReadLines(string filePath) { using (StreamReader file = new StreamReader(filePath)) { string line; while ((line = file.ReadLine()) != null) { yield return line; } } } ``` 这段程序能够按需从指定路径下的文本文件每行依次取出内容作为单独项目传递出去供外部循环消费[^5]。 #### 错误处理机制 如果希望在遍历过程中抛出异常,则可以直接放置常规 try-catch 结构包围可能失败的操作部分即可正常工作。需要注意的是,一旦发生未被捕获错误则剩余尚未产生的成员都不会再有机会被访问到了。 另外还存在另一种形式叫做 `yield break`, 它的作用相当于提前终止当前正在进行中的列举过程而不必等到自然完成条件满足为止[^6]: ```csharp foreach(var item in collection){ if(someCondition) { yield break; }else{ yield return item; } } ``` 以上实例说明了怎样依据特定情况决定是否停止进一步产出新对象的行为。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值