使用 lock() 的 C# 事件监听器仍然抛出异常

标签 c# list events exception xna

我在做一个xna游戏,问题涉及到以下几个类:
游戏类——该类监听下面两个类的事件监听器
Player类——这个类发起一个Fire()事件,告诉游戏玩家发射了一颗子弹
Bullet 类 - 此类启动一个 SelfDestruct() 事件(在一定行进距离后),告诉游戏必须删除该实例

游戏类有一个子弹列表,在更新方法中它在这个列表上做一个 foreach
Fire() 的事件监听器将新项目符号添加到项目符号列表 SelfDestruct() 的事件监听器从列表中删除发件人(通过转换为项目符号)

这两个事件以及更新方法都会锁定列表以确保线程安全。 但它仍然抛出异常,告诉列表在 foreach 期间被修改。

我该如何解决这个问题;因为我确实锁定了列表..但这不起作用:

Update:
public void Update(GameTime gameTime)
{
    player.Update(GameTime gameTime);//can throw fire event
    lock(Bullets)//Lock the list for thread safett
    {
        foreach(Bullet b in Bullets)//Throws exception when bullet is added/removed
            b.Update(gameTime);//can throw selfdestruct event
    }
}

Fire listener:
void listen_fire(object sender, EventArgs e)
{
    Player p = (Player)sender;/used to get coordinates and rotation stored in the player
    lock(Bullets)
    {
        Bullets.Add(new Bullet(p.Position,p.Rotation));
    }
}

Self destruct listener:
void listen_selfdestruct(object sender, EventArgs e)
{
    lock(Bullets)
    {
        Bullets.Remove((Bullet)sender);
    }
}

我认为这个解决方案可能会失败,因为事件是在一个线程中抛出的,该线程本身已经锁定了列表

欢迎任何解决方案,感谢阅读我的问题

最佳答案

foreach 中使用的集合是不可变的。这在很大程度上是设计使然。

正如它在 MSDN 上所说的那样:

The foreach statement is used to iterate through the collection to get the information that you want, but can not be used to add or remove items from the source collection to avoid unpredictable side effects. If you need to add or remove items from the source collection, use a for loop.

例如这段代码会抛出异常:

     List<string> lst = new List<string>();

     lst.Add("aaa");
     lst.Add("bbb");

     foreach (string curr in lst)
     {
        if (curr.Equals("aaa"))
        {
           lst.Remove(curr);
        }
     }

所以我这样做是为了在迭代时不会从列表中删除:

     List<string> lst = new List<string>();

     lst.Add("aaa");
     lst.Add("bbb");
     List<string> lstToDel = new List<string>();

     foreach (string curr in lst)
     {
        if (curr.Equals("aaa"))
        {
           lstToDel.Add(curr);
        }
     }

     foreach (string currToDel in lstToDel)
     {
        lst.Remove(currToDel);
     }

现在我不知道你的 Bullets 中有哪些项目,但你不能在 foreach 语句中更新或删除它们

关于使用 lock() 的 C# 事件监听器仍然抛出异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18555711/

相关文章:

events - Safari for Mac 桌面版 : HTML 5 audio "ended" event doesn't fire problem

c# - 使用服务访问控制台应用程序中的服务需要证书

c# - 如何使用 Global.asax 来使用 Local_resources?

c# - WPF 图像控件源绑定(bind)

r - 如何将空列添加到 r 中的数据框列表

php - 触发 Laravel 事件

c# - @Html.DisplayFor() 不适用于自定义模型子类

list - 从 lisp 中的列表中删除一个元素

python - 为什么在使用带有 .append() 方法的 map() 时返回四个列表

events - Native 和 ReactJS 事件的排序