有了这个.NET事件,传入这个IList实例是否可以?

时间:2010-09-23 00:02:45

标签: .net events list collections

我有以下代码: -

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 ...然后清除原始列表?

4 个答案:

答案 0 :(得分:5)

忘记清理,你正在改变列表的事实会对订阅你的事件的对象造成严重破坏。 如果它们保留了返回的列表,那么只要您添加到列表中清除它,您就会更改数据。

您不仅可以搞砸订阅者,他们还可以搞乱,因为他们可能会改变列表并影响您自己的流程。

如果这不是理想的行为(并且我认为不是这样),那么您甚至不想发送副本,而是发送IEnumerable<Foo>ReadOnlyCollection<Foo>。因为即使您发送了一份副本,如果您有多个订阅者,他们也会收到相同的副本,因此他们的突变仍会对彼此造成严重破坏。

答案 1 :(得分:2)

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

但是,您应该避免传递列表本身,您应该传递一份副本。否则事件订阅者可以保持对列表的引用并以不可预测的方式弄乱它......

答案 2 :(得分:2)

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

答案 3 :(得分:1)

我希望通过评论来做到这一点,但没有声誉: - )

Anthony's answer的偏执扩展,你无法控制接收者,是为了确保你传出的列表副本是ReadOnlyCollection<Foo>(*)。如果您只是将列表复制为另一个List<Foo>或其他可变集合,即使CustomFooEventArgsFoos定义为IEnumerable<Foo>之类的非可变接口,也可能是一个特别卑鄙的接收者将Foos强制转换为List<Foo>并将其变异。

对于Thomas' answer,除了我遗漏某些内容之外,没有任何东西可以阻止事件接收器使用BeginInvoke生成异步任务。

(*)我可能在IEnumerable<Foo>中将其定义为CustomFooEventArgs,但使用ReadOnlyCollection<Foo>的事实是实施细节恕我直言。