Unity中C#的foreach循环未用break却提前退出?
Unity NativeList foreach循环提前退出问题
场景与代码实现
需求是统计NativeList<int>中重复ID的出现次数,初始代码如下:
主循环逻辑
int currentId = -1; int count = 0; foreach (var item in rawItemList) { UnityEngine.Debug.LogFormat("Item {0}", item); if (currentId != item) { AddItem(currentId, count); count = 0; } currentId = item; count++; UnityEngine.Debug.LogFormat("Count {0}", count); }
AddItem辅助函数
void AddItem(int itemId, int count) { UnityEngine.Debug.LogFormat("Add item {0} count {1}", itemId, count); if (count == 0) return; items.Add(itemId); counts.Add(count); }
其中rawItemList、items、counts均为NativeList<int>类型,rawItemList包含元素:{0, 0, 2, 2, 3, 4, 4},循环前后打印该列表均能正常输出全部元素。
异常现象
第二次调用AddItem后,foreach循环直接终止,未遍历rawItemList剩余元素。控制台日志如下:
Item 0 Add item -1 count 0 Count 1 Item 0 Count 2 Item 2 Add item 0 count 2 Count 1 // 循环在此提前退出
补充信息
编辑1:改用for循环后运行正常
将foreach替换为标准for循环后,代码执行完全符合预期,疑问:foreach循环存在什么问题?
编辑2:完整结构体代码与调用逻辑
// public struct CountedItemList ... public NativeList<int> items; public NativeList<int> counts; public CountedItemList(NativeList<int> rawItemList) { items = new NativeList<int>(Allocator.Temp); counts = new NativeList<int>(Allocator.Temp); if (rawItemList.Length == 0) return; rawItemList.Sort(); int currentId = -1; int count = 0; foreach (var item in rawItemList) { UnityEngine.Debug.LogFormat("Item {0}", item); if (currentId != item) { AddItem(currentId, count); count = 0; } currentId = item; count++; UnityEngine.Debug.LogFormat("Count {0}", count); } AddItem(currentId, count); }
调用代码:
NativeList<int> rawItemList = new NativeList<int>(Allocator.Temp); // 填充元素逻辑 var cil = new CountedItemList(rawItemList); rawItemList.Dispose();
编辑3:正常运行的for循环实现
for(int i = 0; i < rawItemList.Length; i++) { int item = rawItemList[i]; // 后续统计逻辑与原foreach一致 }
问题原因
这是Unity NativeList的迭代器特性导致的:
NativeList的foreach循环依赖内部的**NativeArray<T>.Enumerator**,该迭代器在创建时会固定捕获NativeList当前的内存地址和元素长度,后续不会主动更新。- 循环内调用的
AddItem会对items和counts执行Add操作,当这两个NativeList的容量不足时,Unity会触发非托管内存重新分配:申请新内存块、复制旧数据、释放原内存。 - 这种内存操作会干扰
rawItemList迭代器的内部状态,导致迭代器误判为已遍历到末尾,从而提前终止循环。
而for循环通过索引直接访问NativeList元素,每次访问都会实时读取rawItemList的当前内存地址和长度,完全不依赖迭代器对象,因此不受其他NativeList内存操作的影响。
内容的提问来源于stack exchange,提问作者Cansisti
相关产品推荐
相关产品推荐

