Something behind yield

C# yield 关键字详解
本文详细介绍了 C# 中的 yield 关键字,探讨了使用 yield 的优势及限制条件,包括如何用 yield 实现迭代器,以及在何种情况下不能使用 yield。通过具体的代码示例解释了 yield 的工作原理。

Today I spent quite some time on digging into C# yield keyword. It is one of the hard areas in C# for me to understand. I started by reading the C# reference but it didn’t help me too much, until I found this article on MSDN magazine.

 

 

Overview

If you need to return a collection in one function, you can choose to return a collection (such as List<T>) directly, or return an IEnumerator for the outside loop to iterate. The first one is easy to implement but there might have performance defects when the collection is huge – for every call of our function there will be a List<T> object generated on GC heap, even if you only want to process the first several objects. The 2nd solution is much simpler and more memory effective however there are several issues attached with it:

1)    Hard to implement IEnumerators for complicated data structures like binary trees. Our IEnumerator implementation has to maintain the state of the current visit and the code is really complex and error leading in this case.

2)    If you are going to provide several different iterators for a collection, our code will be bloated with various iterator implementations.

 

yield keyword (introduced in C# of .Net 2.0) is provided to address the above two issues. With yield there is no need for you to worry about remembering state for the iterator anymore. All you need to do is to tell what to yield and the compiler will automatically generate the IEnumerator implementation for you automatically.

 

public class EnumerableTest: IEnumerable<int>

{

IEnumerator<int> IEnumerable<int>.GetEnumerator()

{

yield return 0;

yield return 1;

for(int i = 2; i < 5; i++)

{

yield return i;

if (i > 3)

{

yield break;

}

}

}

IEnumerator IEnumerable.GetEnumerator()

{

return ((IEnumerable<int>)this)GetEnumerator();

}

}

 

A simple way to understand yield is as follows: Compiler generated code returns each value you yield in your code (with regards to the order they appear in the code), and pause the execution of the method from that place. When next iteration comes, the method will resume execution from where it is paused and next value is returned.

 

Iterator Implementation

The C# compiler will generate a nested class for the code containing the yield keyword. The class is an implementation of the IEnumerator<T> interface and encapsulates the state needed to iterate within the parent class – here a simple state machine is used to achieve this. When the iterator is first called a new iterator object is created and it’ll be used throughout the remaining iterations. The iterator object doesn’t persist across loops so it is safe for you to call foreach again because you’ll get a brand new iterator object to start the new iteration.

 

There are plenty of articles about this so I don’t want to put more details here.

 

Limitations Explained

yield can appear in any iterator block, where the iterator block can be function, property, indexer etc. There are some limitations where you can use yield though:

1)    You cannot have a return in the function or property where yield is used.

This is obvious because a return will break the iteration improperly.

2)    yield cannot be used in a try block which has a corresponding catch block (finally block is OK).

This is not very intuitive but not very hard to understand. It’s all about the complexity of supporting this.

IEnumerable<int> Content()

{

      try

{

      int val = CalculateVal(…);

      yield return val;       // You’ll get a compilation error here.

}

catch (SomeException e)

{

}

}

Suppose the CalculateVal throws an exception, there will be nothing to return for this iteration, which breaks the iterator contract. A possible solution is to move to calculate the next value. Remember this needs to be tracked by the state machine – you can guess out the complexity I believe. Furthermore, is this solution really the wanted behavior for everybody? I doubt.

3)    yield cannot be used within anonymous methods.

Hmm, with this limitation we lose the ability to use yield in lambda expressions. What a pity!

foreach (int sth in (x => { for (int y=0; y<4; y++) { yield return 2*y; y++; } }))  // You’ll get another 2 compilation errors here.

{

     

}

It is said that Microsoft is going to support using yield within anonymous methods in the future. I am looking forward to that.

 

Return Type of Iterator Block

You can return either IEnumerator or IEnumerable for your iterator block using yield. The hidden state machine is always an IEnumerator, and it’s exactly the same code whether you specify IEnumerator or IEnumerable.

Most of the time specifying IEnumeralbe is better because the compiler will automatically generate some kind of “outer” class and provides an implementation of GetEnumerator, which simplifies your code a lot.

 

When would you want to return IEnumerator then? The answer is: it’ll be very useful if you would like to return multiple iteration methods. For example you want to provide pre-order, in-order and post-order iteration for a binary tree, you might use following code to achieve this. This thread has more about this.

 

public class BinaryTree

{

public IEnumerator<Node> PreOrder()

{

…yield return…

}

 

public IEnumerator<Node> PreOrder()

{

…yield return…

}

 

public IEnumerator<Node> PreOrder()

{

…yield return…

}

}

 

yield.jpg

Delphi 12.3 作为一款面向 Windows 平台的集成开发环境,由 Embarcadero Technologies 负责其持续演进。该环境以 Object Pascal 语言为核心,并依托 Visual Component Library(VCL)框架,广泛应用于各类桌面软件、数据库系统及企业级解决方案的开发。在此生态中,Excel4Delphi 作为一个重要的社区开源项目,致力于搭建 Delphi 与 Microsoft Excel 之间的高效桥梁,使开发者能够在自研程序中直接调用 Excel 的文档处理、工作表管理、单元格操作及宏执行等功能。 该项目以库文件与组件包的形式提供,开发者将其集成至 Delphi 工程后,即可通过封装良好的接口实现对 Excel 的编程控制。具体功能涵盖创建与编辑工作簿、格式化单元格、批量导入导出数据,乃至执行内置公式与宏指令等高级操作。这一机制显著降低了在财务分析、报表自动生成、数据整理等场景中实现 Excel 功能集成的技术门槛,使开发者无需深入掌握 COM 编程或 Excel 底层 API 即可完成复杂任务。 使用 Excel4Delphi 需具备基础的 Delphi 编程知识,并对 Excel 对象模型有一定理解。实践中需注意不同 Excel 版本间的兼容性,并严格遵循项目文档进行环境配置与依赖部署。此外,操作过程中应遵循文件访问的最佳实践,例如确保目标文件未被独占锁定,并实施完整的异常处理机制,以防数据损毁或程序意外中断。 该项目的持续维护依赖于 Delphi 开发者社区的集体贡献,通过定期更新以适配新版开发环境与 Office 套件,并修复已发现的问题。对于需要深度融合 Excel 功能的 Delphi 应用而言,Excel4Delphi 提供了经过充分测试的可靠代码基础,使开发团队能更专注于业务逻辑与用户体验的优化,从而提升整体开发效率与软件质量。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值