创建一个类型的对象IEnumerable<T>
(例如下面代码示例中的numbers
类型的对象IEnumerable<int>
)是否意味着在堆上创建一个最终需要被垃圾收集的对象?
例如,考虑本博客文章中描述的以下迭代器
IEnumerable<int> GetOneTwoThree() {
yield return 1;
yield return 2;
yield return 3;
}
然后使用它
var numbers = GetOneTwoThree();
foreach (var number in numbers) {
Console.WriteLine(number);
}
简短回答:是的,在这种情况下
除此之外:您实现的那一刻
IEnumerable<T>
,就意味着您的迭代器被输入为IEnumerator<T>
,因此即使它实际上是以值类型实现的:当被输入为接口时,它也会被装箱(因此:堆分配)。更长的答案:可以
struct
编写使用(甚至)的手写迭代器ref struct
,然后可以在没有任何堆分配的情况下使用 - 只要明确使用该类型而不是通过IEnumerable<T>
/IEnumerator<T>
接口 -List<T>
就是一个例子,使用自定义值类型迭代器。但是,这排除了使用yield return
- 它必须是手写的。幸运的foreach
是是鸭子类型的,因此可以有效地使用这种自定义迭代器(当GetEnumerator()
方法返回具有所需功能的定制内容时)。