4
\$\begingroup\$

In unity, in C#, I have the following code in Awake():

Awake()
{
    Transform obj = InstantiateTarget();
    //the bizarrely required and "working" loop
    while (transform.childCount > 0)
    {
        //assign all current children to the replacement object
        foreach (Transform t in transform)
        {
            t.parent = obj;
        }
    }

    Destroy(gameObject);
}

Without the while loop, some random set of the transforms children are destroyed by that end Destroy. With the while, it works exactly as expected (all transform's children become obj's children).

Also, surely with it, there should be an infinite loop (if nothing is changing in another thread, as implied by unity docs). This is in Unity 3.5.

Why does it only work with the while loop? And why doesn't unity get stuck in an infinite loop?

\$\endgroup\$
6
  • \$\begingroup\$ "With the while, it works exactly as expected." - Which is? What is the expected output? \$\endgroup\$
    – user15805
    Mar 28, 2013 at 14:58
  • \$\begingroup\$ The expected output: parent all children to obj. The output without the while loop: only some children are parented to obj, some remain parented to transform \$\endgroup\$
    – Matt Bond
    Mar 28, 2013 at 15:00
  • 1
    \$\begingroup\$ You're getting good answers here, though I believe this belongs on stackoverflow. \$\endgroup\$
    – House
    Mar 28, 2013 at 16:29
  • 1
    \$\begingroup\$ Technically, if Unity did not suppress the thrown exception, you would've noticed the issue right away. The answer requires knowledge of this (Unity) bug so, in my opinion, this is the place for the question. \$\endgroup\$
    – user15805
    Mar 28, 2013 at 17:11
  • 1
    \$\begingroup\$ You're iterating over a collection while removing elements from it (which is what changing its parent will do). This is illegal and produces undefined behavior (or oddness like only iterating over a subset of all items in the collection) for most data structures in almost all languages. This is a basic programming question, nothing super specific to games, C#, or Unity. \$\endgroup\$ Mar 30, 2013 at 2:59

1 Answer 1

13
\$\begingroup\$

It's actually Unity's fault for being quite confusing here. You are not allowed to alter a collection that you're enumerating with a foreach loop. Suppose this:

List<string> SomeList = new List<string>();
//add items to the list
foreach (string s in SomeList)
{
    SomeList.Remove(s); //WRONG! You cannot alter the collection here. Use a for loop instead!
}

Normally, this is not allowed in Mono and throws an exception. Unity apparently suppresses it and tells you nothing. Here's what actually happens in your code:

Awake()
{
    Transform obj = InstantiateTarget();
    //While there are no children attached to this transform;
    while (transform.childCount > 0) /*Not an infinite loop
                                     because on each step you're removing
                                     a child from this transform
                                     (read on for the explanation).*/
    {
        //Try to attach each child to obj
        foreach (Transform t in transform)
        {
            t.parent = obj; /*The first "t" gets attached, but you're also 
                            altering the collection (this.transform loses track of t). 
                            The foreach loop stops completely! 
                            Since you're executing the foreach loop for 
                            as long as this.transfom has children, you 
                            essentially remove one child transform at a time on 
                            each 'while' pass.*/
        }
    }
    Destroy(gameObject);
}

Use a for loop instead and, as a rule of thumb, every time you write a foreach loop, ask yourself if you're not actually trying to remove items from the collection. If you are, stop right there and use the for loop instead.

\$\endgroup\$
2
  • 5
    \$\begingroup\$ C#'s foreach behavior in a nutshell, good catch (pun) on Unity subverting the exception and pretending nothing happened. \$\endgroup\$ Mar 28, 2013 at 15:46
  • 1
    \$\begingroup\$ Argh, of course! I didn't even think that I was modifying a collection, as I was changing the parent of a member of it. Of course it makes sense that it immediately affect transform, I just didn't connect the dots far enough! Thanks for the enlightenment! \$\endgroup\$
    – Matt Bond
    Mar 28, 2013 at 16:34

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .