我有以下代码: -
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的一些while / loop条件。然后每100个foo我发起一个事件,将foo的列表传递给订阅者。然后我清除这个foos列表。一旦完成循环,我就会将剩余的foo发送给订户。
所以 - 如果我触发一个事件,其中包含foo的列表......然后我清除那个列表..这是否意味着订阅者可能获得该列表,现在是空的?我是否应该通过列表的COPY ...然后清除原始列表?
答案 0 :(得分:5)
忘记清理,你正在改变列表的事实会对订阅你的事件的对象造成严重破坏。 如果它们保留了返回的列表,那么只要您添加到列表中或清除它,您就会更改数据。
您不仅可以搞砸订阅者,他们还可以搞乱您,因为他们可能会改变列表并影响您自己的流程。
如果这不是理想的行为(并且我认为不是这样),那么您甚至不想发送副本,而是发送IEnumerable<Foo>
或ReadOnlyCollection<Foo>
。因为即使您发送了一份副本,如果您有多个订阅者,他们也会收到相同的副本,因此他们的突变仍会对彼此造成严重破坏。
答案 1 :(得分:2)
当你清除列表时,事件处理程序已经被执行(它们被同步调用),所以这不是问题。
但是,您应该避免传递列表本身,您应该传递一份副本。否则事件订阅者可以保持对列表的引用并以不可预测的方式弄乱它......
答案 2 :(得分:2)
假设您可以控制所有订阅者,并且只在一个线程上工作,是的,那很好。否则,如果您无法控制订户(谁知道他们将要做什么?)或者您正在使用多个线程处理已发送的集合,则应考虑发送该集合的副本。
答案 3 :(得分:1)
我希望通过评论来做到这一点,但没有声誉: - )
Anthony's answer的偏执扩展,你无法控制接收者,是为了确保你传出的列表副本是ReadOnlyCollection<Foo>
(*)。如果您只是将列表复制为另一个List<Foo>
或其他可变集合,即使CustomFooEventArgs
将Foos
定义为IEnumerable<Foo>
之类的非可变接口,也可能是一个特别卑鄙的接收者将Foos
强制转换为List<Foo>
并将其变异。
对于Thomas' answer,除了我遗漏某些内容之外,没有任何东西可以阻止事件接收器使用BeginInvoke
生成异步任务。
(*)我可能在IEnumerable<Foo>
中将其定义为CustomFooEventArgs
,但使用ReadOnlyCollection<Foo>
的事实是实施细节恕我直言。