Does the creation of an object of type IEnumerable<T>
(such as the numbers
object of type IEnumerable<int>
in the below code example) imply the creation of an object on the heap that has finally to be garbage-collected?
Consider for example the following iterator as described in this blog post
IEnumerable<int> GetOneTwoThree() {
yield return 1;
yield return 2;
yield return 3;
}
and then its usage with
var numbers = GetOneTwoThree();
foreach (var number in numbers) {
Console.WriteLine(number);
}
imply the creation of an object on the heap that has finally to be garbage-collected?
Short answer: Yes, in this case
Apart from anything else: the moment you're implementing IEnumerable<T>
, that means your iterator is typed as IEnumerator<T>
, so even if it was actually being implemented in a value-type: it will be boxed (thus: a heap allocation) when typed as the interface.
Longer answer: it is possible to write hand written iterators that use struct
(or even ref struct
), which then may be used without any heap allocation - as long as the type is used explicitly rather than via the IEnumerable<T>
/IEnumerator<T>
interfaces - List<T>
is an example of this, with a custom value-type iterator. However, that precludes the use of yield return
- it must be hand written. Fortunately foreach
is duck-typed, so can make efficient use of such custom iterators (when the GetEnumerator()
method returns something bespoke that has the required features).