[英]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.