当我的对象不再被引用时,为什么它的事件继续运行?

时间:2012-11-18 04:10:16

标签: c# events event-handling publish-subscribe

假设我正在制作游戏并拥有基础Buff课程。我可能还有一个名为ThornsBuff的子类,它订阅Player类的Damaged事件。

Player可能包含Buff个列表,其中一个可能是ThornsBuff。例如:

测试类

Player player = new Player();
player.ActiveBuffs.Add(new ThornsBuff(player));

ThornsBuff Class

public ThornsBuff(Player player)
{
    player.DamageTaken += player_DamageTaken;
}

private void player_DamageTaken(MessagePlayerDamaged m)
{
    m.Assailant.Stats.Health -= (int)(m.DamageAmount * .25);
}

这只是为了说明一个例子。如果我要从列表中删除buff,则事件不会分离。即使玩家不再拥有buff,事件仍然会像他一样执行。

现在,我可以使用Dispel方法取消注册该事件,但这会强制开发人员除了从列表中删除Dispel之外还要调用Buff。如果他们忘记了,增加了耦合等等。

我不明白的是,当从列表中删除Buff时,事件不会自动解除。 Buff仅存在于列表中,这是它的一个引用。删除之后不应该分离事件吗?

我尝试将分离代码添加到Buff的终结器中,但是也没有修复它。即使它有0个引用,该事件仍在运行。我想这是因为垃圾收集器还没有运行?有没有办法让它自动和即时,以便当对象没有引用时,它的所有事件都是未注册的?

感谢。

2 个答案:

答案 0 :(得分:1)

无论Buff是否在ActiveBuffs列表中,都将继续调用事件(player_DamageTaken)。这是因为当你运行DamageTaken + = player_DamageTaken时,它会创建一个从player.DamageTaken到 ThornsBuff 实例及其player_DamageTaken方法的引用。 (否则,运行时如何知道哪个ThornsBuff实例可以调用player_DamageTaken?)

在您调用player.DamageTaken - = player_DamageTaken之前,该引用将继续存在。您将Buff添加到ActiveBuffs的事实只是将另一个引用添加到buff中,这在技术上是不必要的(但在我看来这是好形式,因为我不喜欢喜欢挂着未引用的事件处理程序。)

现在你可能想知道如何在不让开发人员调用额外的Dispel或Dispose方法的情况下删除事件处理程序,为此以下是一些建议:

  1. 在player_DamageTaken开始时,检查是否有player.ActiveBuffs.Contains(this),如果没有,则调用player.DamageTaken - = player_DamageTaken并返回。这样做的缺点是,每次受到伤害时,你都会搜索整个Buff名单。

  2. 使ActiveBuffs成为一个ObservableCollection(或类似的东西),可以在从列表中删除buff时监听。如果是,您可以自动调用buff.Dispel,以便开发人员不必记住调用它。

答案 1 :(得分:0)

  

我不明白为什么当从列表中删除Buff时事件不会自动解除。

因为将Buff附加到列表并不是附加事件的内容。附加事件是因为您将其附加到构造函数中。玩家类实例现在也在其DamangeTake事件中引用了该buff,因此将其从Buffs列表中删除是不足以允许对象被垃圾收集...因此buff继续存在。

人们想知道为什么你在这里使用事件。为什么不在播放器中使用代码来迭代列表中的每个buff,并运行一些定义为接口一部分的方法,将损坏作为参数或返回值。那会更多你正在寻找的东西。

或者,在您目前删除buff的任何地方,您还需要代码来取消订阅它的事件。