简体   繁体   English

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

[英]Event delegates disappear after being added in a foreach loop

I am trying to add event delegates to the objects I have got in a list. 我试图将事件委托添加到列表中的对象。 In the following example, I want to add the dinosaur_Jumped delegate whenever an object fires the associated event. 在下面的示例中,我想在对象触发关联事件时添加dinosaur_Jumped委托。 I am adding them in a foreach loop, but somehow they disappear straight after that. 我将它们添加到一个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;
}

I am presuming it has something to do with incorrect scope/closures or something; 我以为它与不正确的作用域/关闭或某些原因有关; adding a delegate to a variable (in this case dino ) that goes out of scope immediately, but I don't know how else I could this. 将委托添加到立即超出范围的变量(在本例中为dino )中,但是我不知道该怎么办。 Why doesn't this work? 为什么不起作用?

As I cannot see how you are checking your Jumped delegates, I will assume you are performing a subsequent iteration of m_dinosaurs . 因为我看不到您如何检查已Jumped委托,所以我假设您正在执行m_dinosaurs的后续迭代。

Because Select is lazy, any subsequent iterations will result in different DinosaurViewModel instances being created, this means you are inspecting different instances to those which have had the event handler added. 由于Select是惰性的,因此任何后续迭代都将导致创建不同的DinosaurViewModel实例,这意味着您要检查与添加了事件处理程序的实例不同的实例。

A solution to this is to materialize the collection, eg 一个解决方案是实现集合,例如

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

Possible Garbage Collection 可能的垃圾收集

A less likely but possible situation results from the garbage collector, your Select statement will create a new DinosaurViewModel for each iteration, when you iterate m_dinosaurs and add the event handler the newly created DinosaurViewModel becomes eligible for garbage collection as there is nothing keeping a reference to it. 不太可能但可能的情况是由垃圾收集器导致的,您的Select语句将为每次迭代创建一个新的DinosaurViewModel ,当您迭代m_dinosaurs并添加事件处理程序时,新创建的DinosaurViewModel就有资格进行垃圾收集,因为没有任何东西可以保留m_dinosaurs的引用。它。

The solution is to ensure you keep a reference to each created DinosaurViewModel , the same solution as above will be sufficient as the .ToList() call will ensure a reference to each created DinosaurViewModel is retained, this means they are no longer eligible for garbage collection. 解决方案是确保您保留对每个创建的DinosaurViewModel的引用,与上述相同的解决方案就足够了,因为.ToList()调用将确保保留对每个创建的DinosaurViewModel的引用,这意味着它们不再符合垃圾回收的条件。 。

Enumerable.Select is lazy. Enumerable.Select是惰性的。 Very lazy. 非常懒。 So lazy, in fact, that it completely disregards any output you've already seen from it. 实际上,它是如此懒惰,以至于它完全忽略了您已经从中看到的任何输出。 When you iterate over m_dinosaurs a second time, you get a completely fresh batch of DinosaurModel objects. 当您第二次遍历m_dinosaurs时,您将获得一批全新的DinosaurModel对象。

You may use dinosaurs.ConvertAll( x => new DinosaurViewModel(x) ) instead, to store the models in a list. 您可以改用dinosaurs.ConvertAll( x => new DinosaurViewModel(x) )将模型存储在列表中。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM