事件委托在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
)添加一个委托,但我不知道还能怎么做。为什么不起作用?
由于我看不出您是如何检查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) )
将模型存储在列表中。