C# 事件委托在foreach循环中添加后消失
我正在尝试将事件委托添加到列表中的对象中。在下面的示例中,我想在对象触发关联事件时添加C# 事件委托在foreach循环中添加后消失,c#,linq,events,C#,Linq,Events,我正在尝试将事件委托添加到列表中的对象中。在下面的示例中,我想在对象触发关联事件时添加恐龙委托。我将它们添加到一个foreach循环中,但不知何故,它们会立即消失 class MyViewModel { MyViewModel(List<Dinosaur> dinosaurs) { // This works and creates the ViewModel the way I expect it to: m_dinosaurs =
恐龙
委托。我将它们添加到一个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;
}
类MyViewModel
{
MyViewModel(列出恐龙)
{
//这将按照我期望的方式工作并创建ViewModel:
m_恐龙=恐龙。选择(x=>新恐龙视图模型(x));
foreach(恐龙视图m_恐龙中的恐龙模型)
{
//这在循环的范围内工作
恐龙跳跃+=恐龙跳跃;
}
//但现在我所有的代表突然都不见了
}
无效恐龙_跳跃(对象发送器,跳跃事件参数e)
{
//即使发生以下事件,也不会调用此命令:
控制台。WriteLine(“一只恐龙跳了起来”);
}
私人可数恐龙;
}
我认为这与不正确的作用域/闭包或其他东西有关;将委托添加到立即超出范围的变量(在本例中为
dino
)中,但我不知道如何才能做到这一点。为什么不起作用?因为我看不出您是如何检查您的跳过的代理的,所以我假设您正在执行m_恐龙的后续迭代
由于Select
是惰性的,任何后续迭代都将导致创建不同的viewmodel
实例,这意味着您将检查添加了事件处理程序的不同实例
解决这个问题的一个方法是将集合物化,例如
m_dinosaurs = dinosaurs.Select( x => new DinosaurViewModel(x) ).ToList();
可能的垃圾收集
垃圾收集器会导致一种不太可能但可能的情况,您的Select
语句将为每个迭代创建一个新的viewmodel
,当您迭代m_digoros
并添加事件处理程序时,新创建的digoraviewmodel
将符合垃圾收集的条件,因为没有保留对它的引用
解决方案是确保您保留对每个创建的恐龙视图模型的引用,与相同的解决方案就足够了。ToList()
调用将确保保留对每个创建的恐龙视图模型的引用,这意味着他们不再有资格进行垃圾收集。因为我看不出您是如何检查您的跳过的代理的,所以我假设您正在执行m_恐龙的后续迭代
由于Select
是惰性的,任何后续迭代都将导致创建不同的viewmodel
实例,这意味着您将检查添加了事件处理程序的不同实例
解决这个问题的一个方法是将集合物化,例如
m_dinosaurs = dinosaurs.Select( x => new DinosaurViewModel(x) ).ToList();
可能的垃圾收集
垃圾收集器会导致一种不太可能但可能的情况,您的Select
语句将为每个迭代创建一个新的viewmodel
,当您迭代m_digoros
并添加事件处理程序时,新创建的digoraviewmodel
将符合垃圾收集的条件,因为没有保留对它的引用
解决方案是确保您保留对每个创建的恐龙视图模型的引用,与相同的解决方案就足够了。ToList()
调用将确保保留对每个创建的恐龙视图模型的引用,这意味着它们不再有资格进行垃圾收集。可枚举。Select
是惰性的。非常懒。事实上,它是如此懒惰,以至于完全忽略了您已经从中看到的任何输出。当你第二次迭代m_恐龙
时,你会得到一批全新的恐龙模型
对象
您可以使用恐龙.ConvertAll(x=>新恐龙视图模型(x))
将模型存储在列表中。可枚举。选择是惰性的。非常懒。事实上,它是如此懒惰,以至于完全忽略了您已经从中看到的任何输出。当你第二次迭代m_恐龙
时,你会得到一批全新的恐龙模型
对象
您可以使用恐龙.ConvertAll(x=>新恐龙视图模型(x))
将模型存储在列表中。这也是我的第一个想法,但这是错误的。linq将在下一个foreach
语句中立即计算。无需调用ToList
,这样做也不会产生任何影响。@PhilipPittle每次迭代foreach
循环后,都不会保留对新创建的视图模型的引用,.ToList()
确保视图模型具体化并保留引用。@PhilipPittle您可以尝试的是一个简单的var e=new int[]{1,2,3}。选择(i=>{Console.WriteLine(i);返回i;});foreach(vari在e中){/*什么都不做*/}foreach(vari在e中){/*再次什么都不做*/}
。你会看到数字被打印了两次。FWIW,我认为垃圾收集在这里不重要。第一批对象是否得到GC-ed并不重要:问题只是第二批对象没有任何事件订阅服务器。即使第一批仍然存在,这也是一个问题。@hvd啊,是的,您是正确的,后续的评估将导致不同的视图模型实例,但是我将在这里留下我的答案,因为我以前被延迟的LINQ和GC咬过!这也是我的第一个想法,但那是错误的。linq将在下一个foreach
语句中立即计算。没有必要打电话