- Home /
Any way to use foreach/for with Dictionary without generating junk?
Since Unity uses an old version of the Mono compiler, there is a bug that causes a heap allocation using foreach (see: http://makegamessa.com/discussion/1493/its-official-foreach-is-bad-in-unity/p1). I use dictionaries a bunch, and I am forced to enumerate over key or value collections of dictionaries (in C#).
Has anyone used viable alternatives to foreach or should I just stomach the allocations? Thanks.
Note that iterating dictionaries goes beyond the $$anonymous$$ono compiler foreach bug. For example iterating the $$anonymous$$eys or Values of a dictionary performs an allocation inside the dictionary implementation.
Answer by gfoot · Apr 29, 2014 at 11:08 PM
You can use the underlying iterators without creating the unnecessary garbage - essentially, you write what you would have liked the compiler to generate for you. The basic pattern is:
var enumerator = collection.GetEnumerator();
while (enumerator.MoveNext()) {
var element = enumerator.Current;
// loop body goes here
}
It is more complex though if the enumerator type is disposable, as you need to dispose of it appropriately, even if exceptions occur in your loop body. There are more details here.
I think in modern parlance you can handle the exceptions more tidily than the linked page does, like this:
using (var enumerator = collection.GetEnumerator())
{
while (enumerator.MoveNext()) {
var element = enumerator.Current;
// loop body goes here
}
}
In practice though I would guess that any enumerator which requires disposal is going to create garbage anyway, and so you might as well just use the "foreach" in that case.
Thanks. What do you mean by disposable (i.e. how would I know it's disposable?)? For the most part my dictionaries use ints or strings as keys. Values are usually classes (typically inheriting from $$anonymous$$onobehaviour). Do you mean implementing the IDisplosable interface as the page says? There are also references to topics related to "unmanaged resources" which I might need to read up on as well.
Also, would it be efficient to just leave out the exception-handling?
I just profiled this:
var enumerator = dummyDic.GetEnumerator();
while (enumerator.$$anonymous$$oveNext()) {
// loop body goes here
}
No garbage generated...interesting! However this version:
using (var enumerator = dummyDic.GetEnumerator())
{
while (enumerator.$$anonymous$$oveNext()) {
string element = enumerator.Current.Value;
//var element = enumerator.Current;
// loop body goes here
}
}
Generates 28 bytes each frame. Not sure why: it tells me that Update() (where this code is) generates 28 bytes, but does not generate which specific function like Enumerator.$$anonymous$$oveNext() or Dispose.
It's the Dispose. Apparently, it's supposed to box according to the official C# spec. Visual Studio doesn't box as an optimization, but $$anonymous$$ono follows the spec. Look here.
The following shouldn't box:
var enumerator = dummyDic.GetEnumerator();
try {
while (enumerator.$$anonymous$$oveNext()) {
string element = enumerator.Current.Value;
//var element = enumerator.Current;
// loop body goes here
}
}
finally {
enumerator.Dispose();
}
So, you say that Dispose causes a box (related to the allocation), and you said that your example shouldn't do any boxing...but you do a Dispose() call in the finally block. Is there a typo in your example?
@bleu: Where's the box in hanger's code? The box occurs in the previous example because of "using". The "using" in $$anonymous$$ono incorrectly casts the enumerator to an object, even though it may be a struct. hanger's code has no box because the enumerator is an explicit type (albeit hidden behind var, but var enumerator is compiled into a true type at compile time.) Thus, if GetEnumerator returns a struct, the code treats it as a struct, and the struct will pop off the stack, no garbage. The explicit call to Dispose is just a call to a method on a struct... which doesn't inherently create garbage either.
Your answer
![](https://koobas.hobune.stream/wayback/20220613143907im_/https://answers.unity.com/themes/thub/images/avi.jpg)
Follow this Question
Related Questions
Multiple Cars not working 1 Answer
Distribute terrain in zones 3 Answers
Simple question about targetting 0 Answers
Question on Using a Foreach Loop on Nested Children 1 Answer
Unity compilation order 2 Answers