事件代表在foreach循环中添加后消失

时间:2014-09-03 11:59:39

标签: c# linq events

我正在尝试将事件委托添加到列表中的对象中。在以下示例中,我想在对象触发关联事件时添加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),但我不知道我怎么能这样做。为什么这不起作用?

2 个答案:

答案 0 :(得分:3)

由于我无法看到您如何检查Jumped代表,我将假设您正在执行m_dinosaurs的后续迭代。

因为Select是惰性的,所以任何后续迭代都会导致创建不同的DinosaurViewModel实例,这意味着您正在检查已添加事件处理程序的实例。

对此的解决方案是实现集合,例如

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

可能的垃圾收集

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

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

答案 1 :(得分:2)

Enumerable.Select很懒。非常懒。事实上,这很懒惰,它完全忽视了你已经从中看到过的任何输出。当您第二次迭代m_dinosaurs时,您将获得一批全新的DinosaurModel个对象。

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