foreach, 用还是不用,这是一个问题~ - 腾讯云开发者社区-腾讯云
测试
测试的 Unity 版本为 2021 。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Foreach : MonoBehaviour
{
private readonly int[] _array = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
private readonly string[] _string_array = new string[] { "1", "1", "1", "1", "1", "1", "1", "1", "1", "1" };
private readonly ArrayList _array_list = new() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
private readonly List<int> _list = new() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
private void Update()
{
ForeachArray();
ForeachStringArray();
ForeachArrayList();
ForeachList();
}
private void ForeachArray()
{
foreach (var element in _array) Func(element);
}
private void ForeachStringArray()
{
foreach (var element in _string_array) Func(element);
}
private void ForeachArrayList()
{
foreach (var element in _array_list) Func(element);
}
private void ForeachList()
{
foreach (var element in _list) Func(element);
}
private void Func<T>(T element)
{
}
}
结果
Unity Profiler 的结果如下,可以看到只有在 foreach 中使用 ArrayList 会引起 GC 。
开启 Deep Profile ,进一步确定 GC 是由 ArrayList.GetEnumerator() 引起的。 虽然 List<T> 也会调用 GetEnumerator() ,但是不会 Alloc ,Array 不会调用 GetEnumerator() 。
解释
- 以前版本的 Unity 使用 foreach 会产生额外的 GC ,这实际上是个 Bug ,因为直接使用 Enumerator 就可以规避,目前这个 Bug 已经被修复了。
- List 的 Enumerator 是一个 struct ,所以不会引起 GC 。
- ArrayList 在
GetEnumerator() 时会返回一个 ArrayListEnumeratorSimple 的实例,而这个东西是 class ,所以会调用 GC 。
- 虽然构建生成的 Assembly-CSharp.dll 中的确有
callvirt GetEnumerator() 但是 ILSpy 和 Jetbrains decompiler 反编译出的代码没有出现 using(...) ,可能是反编译得太高级了。
|