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