Be careful using transform in a foreach loop when changing the parent, as the foreach loop will not cycle through all the immediate children. I thought i would post this since I spent the last two hours trying to figure out why all my UI objects were not getting deleted and creating duplicates. It would be nice not to have a prebuilt list since can create temporary objects that will get thrown into garbage collection.
first build a gameobject with several children.
GameObject parent = new GameObject();
for(int i= 0; i <5;i++)
Instantiate(GameObject.CreatePrimitive( PrimitiveType.Sphere), parent);
here is a sample of code that doesn’t work as intended, and only some children will executed on.
int count = 0;
foreach (Transform trans in parent.transform)
{
//Debug.Log("BuildMenu : Clear Item : " + count);
trans.SetParent(null);
//Destroy(trans.gameObject);
count++;
}
here is sample code that will show that a prebuilt list can be used if you want to change the parent during loop.
int count = 0;
List<Transform> list = new List<Transform>();
//build a list to iterate through, cannot use getcomponentsinchildren, as that will find grand descendants
foreach (Transform trans in parent.transform)
list.Add(trans);
//since changing parent of prefab to cache gameobject, cannot use parent.transform directly, have to build with list first.
foreach (Transform trans in list)
{
//Debug.Log("BuildMenu : Clear Item : " + count);
trans.SetParent(null);
//Destroy(trans.gameObject);
count++;
}
I know it’s an archaic method so most people don’t know about it, but the Transform class has a method called DetachChildren which unparents all children.
If you need to do some filtering there’s no way to iterate over the actual children. Here you have several solutions. The foreach enumerator of the Transform class just uses childCount and GetChild internally in a loop. However as usual it iterates through the children forward. When doing it manually you can simply iterate backwards and you get rid of the issues.
for(int i = trans.childCount-1; i >=0; i--)
{
var t = trans.GetChild(i);
// destroy / unparent / whatever
}
If you still want to use foreach you have to create a list or array of your children. With Linq there are several simple solutions. Since the Transform class implements the untyped IEnumerable interface you can use the Cast extension method to cast the untyped IEnumerable to a typed IEnumerable<Transform>. Now you can either use ToList(), ToArray() or just pass the result into a List constructor
var list = trans.Cast<Transform>().ToList();
var list = new List<Transform>(transform.Cast<Transform>());
var array = trans.Cast<Transform>().ToArray();
Of course in order to use any of the Linq extension methods you have to use using System.Linq;
This was a great discussion. I didn’t expect to change my code, but a better solution did work out for me.
i think we can agree foreach is bad. It will likely force garbage collection too. I ended up with code like below, the release object is apart of my object pooling class which sets the parent to null so that objects are cleanly removed from parent panel recttransform in canvas UI. I can turn this into a re-usable method too.
int count = parent.transform.childCount;
while (count > 0)
{
//parent.transform.GetChild(0).SetParent(null);
PoolManager.ReleaseObject("BuildItem", parent.transform.GetChild(0).gameObject);
count--;
}