C# 在更新功能中使用Unity编辑列表大小

C# 在更新功能中使用Unity编辑列表大小,c#,unity3d,C#,Unity3d,假设我有这样一个更新函数: for (int i = 0; i < players.Count; i++) { Debug.Log(players[i].name); } for(int i=0;i

假设我有这样一个更新函数:

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

这个列表正在Update()中使用,所以如果我从另一个脚本/函数中删除任何列表项,它只会抛出一个错误,因为循环进行时列表大小已更改。如何修复此问题?

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

在标准场景中,在调用以下脚本上的下一个更新方法之前,将调用并完成更新方法。Unity worker主线程不会从其他任何地方调用,因此一个脚本不会中断更新方法调用mid函数

现在,如果您说您正在使用co例程,并且在“foreach”循环中让步了,那么在这种情况下,您可能会遇到枚举器抛出错误的情况,因为您可能已经从脚本中的其他地方或其他地方修改了基础枚举(players)

换一种说法,这很糟糕:

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;
        }
    }
}
公共类基:单行为
{
名单;
无效开始()
{
列表=新列表{1,2,3,4};
开始例行程序(ForEachTest());
增加(5);
}
IEnumerator前驱试验()
{
foreach(列表中的变量项)
{
Log($“项值:{Item}”);
收益返回空;
}
}
}
将导致:

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;
}
public bool MoveNext()
{
List=this.List;
如果(版本==列表._版本和(uint)索引<(uint)列表._大小)
{
当前=列表。_项[索引];
索引++;
返回true;
}
返回MoveNextRare();
}
私人bool MoveNextRare()
{
如果(版本!=列表。\u版本)
{
ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion除外);
}
索引=列表。\u大小+1;
电流=默认值(T);
返回false;
}
修改列表时(如使用Count而非Length所示),枚举器会看到集合的版本已被修改,这会使枚举器无效


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

在编程中,如果您使用某个集合,该集合可能会修改您正在使用的集合,则此错误会在编程中随处出现

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

  • 如果可以显示过期信息,请复制一份集合并使用副本
  • 处理所修改集合中出现的任何错误,并使用最新集合重新启动逻辑
  • 选项1:使用副本:

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

    var playersCopy=players.ToList();
    对于(int i=0;i
    使用这种方法,您的内存中有一个无法修改的副本,即使该副本可能已过时

    2) 。如果使用过时副本是不可接受的,那么您需要处理在迭代过程中所修改的集合中出现的异常

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

    bool success=false;
    而(!成功)
    {
    试一试{
    {
    for(int i=0;i
    注意,后面的示例是一个简化的示例,根据您的情况,您可能希望对捕获的错误执行不同的操作

    另外请注意,如果存在副作用,则不建议使用后者,例如,
    Debug.Log
    可能已经打印出一个过时的播放器

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


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

    我认为这与Unity没有任何特别的关系。相反,有一些集合引用了玩家,并在迭代集合时对其进行了修改。这更像是一个一般编程问题,好吧。不,不会。。循环完全运行,然后运行另一个可以添加/删除元素的代码。除此之外,您有一个不同的线程访问该列表。在这种情况下,您应该
    lock
    。另一个引发错误的唯一方法是从该循环中删除/添加元素谢谢。我知道这些解决方案,但我只是想也许有一种简单的方法可以解决这个问题。