创建
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);
}
意味着在堆上创建了一个最终要被垃圾收集的对象?
简短回答:是的,在这种情况下
更长的答案:是可以编写使用
struct
(甚至是ref struct
)的手写迭代器,然后可以在没有任何堆分配的情况下使用 - 只要显式使用类型而不是通过IEnumerable<T>
/IEnumerator<T>
接口 - List<T>
就是一个示例,带有自定义值类型迭代器。然而,这就排除了 yield return
的使用——它必须是手写的。幸运的是 foreach
是鸭子类型的,因此可以有效地利用此类自定义迭代器(当 GetEnumerator()
方法返回具有所需功能的定制内容时)。