2010-09-23 48 views
4

我有下面的代码: -有了这个.NET事件,可以通过这个IList实例吗?

while (....) 
{ 
    var foo = DoTheFooShakeShakeShake(..); 
    foos.Add(foo); // foos is an IList<Foo>, btw and is new'd, above. 

    if (foos.Count % 100 == 0) 
    { 
     var e = new CustomFooEventArgs { UserId = whatever, Foos = foos }; 
     OnFooPewPew(this, e); 
     foos.Clear(); 
    } 
} 

// We still might have some foo's left over.. so send em off also. 
// Excuse the woeful var names, below. 
var e2 = new CustomFooEventArgs { UserId = whatever, Foos = foos }; 
OnFooPewPew(this, e2); 

所以,我抓住所有的foo替换了一会儿/循环状态。然后,每一百美元我都会发起一个事件,它将foo的列表传递给订阅者。然后我清除这个foos列表。一旦循环完成,我然后将任何剩余的foo发送给订阅者。

所以 - 如果我点燃一个事件,其中包含foo的列表...然后我清除该列表..这是否意味着订户可能获得该列表,现在是空的?我是否应该通过列表的COPY ...然后清除原始列表?

回答

5

忘记清理,事实上你根本不会改变清单,这会对预订事件的对象造成严重破坏。 如果他们坚持返回列表,您将随时更改数据,只要您将其添加到列表就可以清除它。

你不仅可以把用户弄乱了,他们可以用,因为他们可以改变列表并影响你自己的过程。

如果这不是我们想要的行为(而且我不认为它是这样),那么您将不想发送一份副本,而是发送一个IEnumerable<Foo>或。因为即使你发送了一份副本,如果你有多个订阅者,他们也会收到相同的副本,所以他们的突变仍然会给彼此造成巨大的破坏。

+0

可变的收藏...肆虐...这是真的... – sloth 2010-09-23 08:08:34

2

当你清除列表时,事件处理程序已经被执行(它们被同步调用),所以这不是问题。

但是,您应该避免传递列表本身,您应该传递一份副本。否则事件用户可以保持对列表的引用,并以不可预知的方式混淆它...

+0

你可以让异步事件被触发/处理吗? – 2010-09-23 00:21:24

+0

是的,如果你用BeginInvoke调用处理程序。但那是你*控制的东西,而不是订户 – 2010-09-23 00:24:55

2

假设你可以控制所有订阅者,并且只在一个线程上工作,那么很好。否则,如果您无法控制订阅者(谁知道他们会做什么?),或者您正在使用多线程处理发送的集合,则应考虑发送该集合的副本。

1

我宁愿有意见要做到这一点,但没有:-)

Anthony's answer,在这里你没有接收器的控制偏执扩展的声誉,是保证列表复制你传递的是一个ReadOnlyCollection<Foo>(*)。如果您仅仅将您的列表复制为另一个List<Foo>或其他可变集合,即使CustomFooEventArgsFoos定义为非易变接口(例如IEnumerable<Foo>),特别卑鄙的接收方也可以将Foos作为List<Foo>投射并对其进行变异。

至于Thomas' answer,使用BeginInvoke停止事件接收器产生一个异步任务,除非我遗漏了一些东西,没有什么能够停止。 (*)我可能会将它定义为CustomFooEventArgs中的IEnumerable<Foo>,但使用的事实是ReadOnlyCollection<Foo>是一个实现细节恕我直言。

相关问题