事件委托在foreach循环中添加后将消失

本文关键字:添加 消失 循环 foreach 事件 | 更新日期: 2023-09-27 18:20:33

我正在尝试将事件委托添加到列表中的对象中。在以下示例中,每当对象触发关联事件时,我都希望添加dinosaur_Jumped委托。我将它们添加到foreach循环中,但不知何故,它们在那之后就消失了。

class MyViewModel
{
    MyViewModel(List<Dinosaur> dinosaurs)
    {
        // This works and creates the ViewModel the way I expect it to:
        m_dinosaurs = dinosaurs.Select( x => new DinosaurViewModel(x) );
        foreach (DinosaurViewModel dino in m_dinosaurs)
        {
            // This works within the scope of the loop
            dino.Jumped += dinosaur_Jumped;
        }
        // But now all my Jumped delegates are suddenly all gone
    }
    void dinosaur_Jumped(object sender, JumpingEventArgs e)
    {
        // This never gets called, even when the events do fire:
        Console.WriteLine("A dinosaur jumped");
    }
    private IEnumerable<DinosaurViewModel> m_dinosaurs;
}

我推测这与不正确的范围/闭包或其他什么有关;向一个立即超出范围的变量(在本例中为dino)添加一个委托,但我不知道还能怎么做。为什么不起作用?

事件委托在foreach循环中添加后将消失

由于我看不出您是如何检查Jumped委托的,我将假设您正在执行m_dinosaurs的后续迭代。

由于Select是惰性的,任何后续迭代都将导致创建不同的DinosaurViewModel实例,这意味着您要检查的实例与添加了事件处理程序的实例不同。

对此的解决方案是将集合具体化,例如

m_dinosaurs = dinosaurs.Select( x => new DinosaurViewModel(x) ).ToList();

可能的垃圾回收

垃圾收集器产生了一种可能性较小但可能发生的情况,Select语句将为每次迭代创建一个新的DinosaurViewModel,当您迭代m_dinosaurs并添加事件处理程序时,新创建的DinosaurViewModel就有资格进行垃圾收集,因为没有任何东西可以保留对它的引用。

解决方案是确保保留对每个创建的DinosaurViewModel的引用,如上所述的解决方案就足够了,因为.ToList()调用将确保保留对每一个创建的DinosaurViewModel的引用,这意味着它们不再有资格进行垃圾收集。

Enumerable.Select是懒惰的。非常懒惰。事实上,它太懒惰了,以至于完全忽略了你已经看到的任何输出。当你第二次迭代m_dinosaurs时,你会得到一批全新的DinosaurModel对象。

您可以使用dinosaurs.ConvertAll( x => new DinosaurViewModel(x) )将模型存储在列表中。