foreach
is an interesting beast; a lot of people mistakenly think it is tied to IEnumerable[<T>]
/ IEnumerator[<T>]
, but that simply isn't the case. As such, it is meaningless to say:
Every iteration of every “foreach” loop generated 24 Bytes of garbage memory.
In particular, it depends on exactly what you are looping over. For example, a lot of types have custom iterators - List<T>
, for example - has a struct
-based iterator. The following allocates zero objects:
// assume someList is a List<SomeType>
foreach(var item in someList) {
// do something with item
}
However, this one does object allocations:
IList<SomeType> data = someList;
foreach(var item in data) {
// do something with item
}
Which looks identical, but isn't - by changing the foreach
to iterate over IList<SomeType>
(which doesn't have a custom GetEnumerator()
method, but which implements IEnumerable<SomeType>
) we are forcing it to use the IEnumerable[<T>]
/ IEnumerator<T>
API, which much by necessity involve an object.
Edit caveat: note I am assuming here that unity can preserve the value-type semantics of the custom iterator; if unity is forced to elevate structs to objects, then frankly all bets are off.
As it happens, I'd be happy to say "sure, use for
and an indexer in that case" if every allocation matters, but fundamentally: a blanket statement on foreach
is unhelpful and misleading.