Unity编辑列表在更新功能中使用时的大小



假设我有一个类似的更新函数:

for (int i = 0; i < players.Count; i++)
{
Debug.Log(players[i].name);
}

这个列表正在Update((中使用,所以如果我从另一个脚本/函数中删除任何列表项,它只会抛出一个错误,因为循环进行时列表大小发生了变化。我该如何解决这个问题?

这个bug在编程中的任何地方都会弹出,因为你正在处理一些可能会修改你正在处理的集合的集合。

根据我的经验,有两种选择:

  1. 如果可以显示过期信息,请复制该集合并使用该副本
  2. 处理正在修改的集合中发生的任何错误,并使用最新的集合重新启动逻辑

选项1:使用副本:

在访问集合之前,请复制内存中的内容。例如,使用.ToList().ToArray()

var playersCopy = players.ToList();
for (int i = 0; i < playersCopy .Count; i++)
{
Debug.Log(playersCopy [i].name);
}

使用这种方法,您在内存中有一个无法修改的副本,即使认为该副本可能已经过时或过时。

2( 。如果使用过时的副本是不可接受的,那么您需要在迭代时处理正在修改的集合中发生的异常。

例如,您可以将对集合的访问权放在try catch中,并在修改集合时重试逻辑。

bool success = false;
while(!success)
{
try {
{
for (int i = 0; i < players.Count; i++)
{
Debug.Log(players[i].name);
}
success=true; // loop completed without error
}
catch { }
}

请注意,后面是一个简化的示例,根据您的情况,您可能需要对捕获的错误执行不同的操作。

还要注意,如果有副作用,则不建议使用后者。例如,Debug.Log可能已经打印出了一个过时的播放器。

这有自己的解决方案,例如:先将玩家名称保存到列表中,然后打印出内存中的列表,或者再次使用自定义逻辑(例如,跟踪哪些玩家不再在列表中,并打印一条消息,说明他们离开了,然后继续列表的其余部分(。

方法仍然是一样的,使用内存中的副本或实时检测更改并进行处理。

幸运的是,根据您提供的信息,这不会引发错误。

在标准场景中,在调用以下脚本上的下一个Update方法之前,将调用并完成Update方法。主Unity工作线程不是从其他地方调用的,所以一个脚本不会在函数中间中断Update方法调用。

现在,如果你说你正在使用协同例程,并且在"foreach"循环中让步了,那么在这种情况下,你可能会遇到枚举器抛出错误的情况,因为你可以从这个脚本或其他脚本中的其他地方修改底层的可枚举(播放器(。

另一种说法是,这很糟糕:

public class Base : MonoBehaviour
{
List<int> list;
void Start ( )
{
list = new List<int> { 1, 2, 3, 4 };
StartCoroutine ( ForEachTest ( ) );
list.Add ( 5 );
}
IEnumerator ForEachTest ( )
{
foreach ( var item in list )
{
Debug.Log ( $"Item value : {item}" );
yield return null;
}
}
}

将导致:

InvalidOperationException:集合已修改;枚举操作可能不会执行。

如果您查看枚举器的代码,您可以了解引发异常的原因。

public bool MoveNext()
{
List<T> list = this.list;
if (version == list._version && (uint)index < (uint)list._size)
{
current = list._items[index];
index++;
return true;
}
return MoveNextRare();
}

private bool MoveNextRare()
{
if (version != list._version)
{
ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);
}
index = list._size + 1;
current = default(T);
return false;
}

当修改List(如您使用Count而非Length所建议的(时,枚举器会看到集合的版本已被修改,从而使枚举器无效。

请注意,如果您不使用枚举器,而只是使用检查列表的Count属性的for循环,则不会遇到此问题。

最新更新