You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#中使用UniRx移除观测列表元素时避免异常的方法

解决UniRx遍历集合时修改引发的InvalidOperationException问题

错误原因

你遇到的InvalidOperationException是因为SelectMany(_ => _gameState.enemies)直接对原集合进行枚举,而在Subscribe回调中修改了原集合(调用Remove)——.NET的集合枚举器不允许在枚举过程中修改集合结构,因此触发了这个异常。

无内存分配的替代方案

最直接的无分配方案是反向遍历原集合,结合UniRx的EveryUpdate驱动操作:

Observable.EveryUpdate()
    .Subscribe(_ =>
    {
        // 反向遍历,避免移除元素导致的索引错乱
        for (int i = _gameState.enemies.Count - 1; i >= 0; i--)
        {
            var enemy = _gameState.enemies[i];
            if (enemy.x > 5)
            {
                _gameState.enemies.RemoveAt(i);
            }
        }
    })

这种方式不需要创建任何新集合,完全在原集合上操作,没有额外内存分配,同时反向遍历不会因为移除元素导致后续元素索引偏移,保证遍历的正确性。

如果希望保留Observable链式风格,还可以复用一个预初始化的临时列表来避免重复分配:

// 提前初始化临时列表,仅分配一次内存
private readonly List<Enemy> _tempRemovalList = new List<Enemy>();

// Observable逻辑
Observable.EveryUpdate()
    .Subscribe(_ =>
    {
        _tempRemovalList.Clear();
        // 先收集需要移除的元素
        foreach (var enemy in _gameState.enemies)
        {
            if (enemy.x > 5)
            {
                _tempRemovalList.Add(enemy);
            }
        }
        // 批量移除
        foreach (var enemy in _tempRemovalList)
        {
            _gameState.enemies.Remove(enemy);
        }
    })

这个方案仅在初始化_tempRemovalList时产生一次内存分配,后续每帧操作都不会新增分配,同时保持了逻辑的清晰性。

其他基于Observable的实现方法

如果不介意少量内存分配(适合元素数量不多的场景),可以用以下链式写法:

1. 使用ToList()/ToArray()提前复制集合

这是你提到的方案,本质是先复制原集合到新列表,避免枚举原集合时被修改:

Observable.EveryUpdate()
    .SelectMany(_ => _gameState.enemies.ToList()) // 复制到新列表,避免枚举原集合
    .Where(enemy => enemy.x > 5)
    .Subscribe(enemy =>
    {
        _gameState.enemies.Remove(enemy);
    })

2. 用Select收集待移除元素后批量处理

Observable.EveryUpdate()
    .Select(_ => _gameState.enemies.Where(e => e.x > 5).ToList())
    .Subscribe(enemiesToRemove =>
    {
        foreach (var enemy in enemiesToRemove)
        {
            _gameState.enemies.Remove(enemy);
        }
    })

这种方式将筛选和移除分离,逻辑更清晰,但每次都会创建新列表,存在一定内存开销。

3. 使用Aggregate聚合待移除元素

Observable.EveryUpdate()
    .Select(_ => 
        _gameState.enemies.Aggregate(new List<Enemy>(), (list, enemy) =>
        {
            if (enemy.x > 5) list.Add(enemy);
            return list;
        }))
    .Subscribe(enemiesToRemove =>
    {
        foreach (var enemy in enemiesToRemove)
        {
            _gameState.enemies.Remove(enemy);
        }
    })

和ToList()方案类似,本质还是创建新集合存储待移除元素,适合需要在链式操作中完成聚合的场景。

内容的提问来源于stack exchange,提问作者Eduardo Páez Rubio

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 11:12:04