T []不比IEnumerable <T>更好作为参数类型吗?(考虑线程挑战)

Mar*_*ing 9 c# collections ienumerable multithreading thread-safety

一些程序员认为最好将IEnumerable<T>参数传递给传递的实现,例如List<T>,这样做的一个常见原因是API可以在更多场景中立即使用,因为更多的集合将与IEnumerable<T>任何其他特定实现兼容,例如List<T>.

我越是深入研究多线程开发场景,我就越开始认为这IEnumerable<T>也不是正确的类型,我将尝试解释下面的原因.

枚举集合时是否收到过以下异常?

收集被修改; 枚举操作可能无法执行.(InvalidOperationException)

集合被修改了异常

基本上导致这种情况的原因是,您在一个线程上获得了集合,而其他人在另一个线程上修改了该集合.

为了解决这个问题,我开始习惯在枚举之前拍摄集合的快照,方法是将集合转换为方法内的数组,如下所示:

static void LookAtCollection(IEnumerable<int> collection)
{   
    foreach (int Value in collection.ToArray() /* take a snapshot by converting to an array */)
    {
        Thread.Sleep(ITEM_DELAY_MS);
    }
}
Run Code Online (Sandbox Code Playgroud)

我的问题是这个.作为一般规则,对数组而不是枚举进行编码不是更好吗,即使这意味着调用者现在被迫在使用API​​时将其集合转换为数组?

这不是更清洁,更防弹吗?(参数类型是一个数组而不是)

static void LookAtCollection(int[] collection)
{   
    foreach (int Value in collection)
    {
        Thread.Sleep(ITEM_DELAY_MS);
    }
}
Run Code Online (Sandbox Code Playgroud)

该数组满足可枚举和固定长度的所有要求,并且调用者知道您将在该时间点对集合的快照进行操作,这可以节省更多错误.

我现在能找到的唯一更好的选择是IReadOnlyCollection,它将更加防弹,因为该集合在项目内容方面也是只读的.

编辑:

@DanielPryden提供了一篇非常好的文章"Arrays被认为有害"的链接.作者的评论"我很少需要一个具有相当矛盾的特性,完全可变,同时又大小固定""几乎在所有情况下都有一个比一个更好的工具使用的集合".阵".有点让我相信阵列不像我希望的那样接近银弹,我同意风险和漏洞.我认为现在IReadOnlyCollection<T>接口是一个比数组和接口更好的选择IEnumerable<T>,但它现在让我有一个问题:被调用者是否通过IReadOnlyCollection<T>在方法声明中使用参数类型来强制执行它?或者是否应该允许调用者决定将哪些实现IEnumerable<T>传递给查看集合的方法?

感谢所有的答案.我从这些回复中学到了很多东西.

sup*_*cat 4

这里真正的“问题”是, whileT[]可能是比理想情况更具体的参数类型,并且允许接收者自由访问写入任何元素(这可能是或可能不是所需的),IEnumerable<T>但过于笼统。从类型安全的角度来看,调用者可以提供对任何实现 的对象的引用IEnumerable<T>,但实际上只有某些此类对象才能真正工作,并且调用者没有干净的方法来知道这些对象是哪些。

如果T是一种引用类型,则 aT[]本质上对于读取、写入和枚举是线程安全的,但要遵守某些条件(例如,访问不同元素的线程根本不会干扰;如果一个线程在另一个线程读取或读取某项时左右写入一项)枚举它,后一个线程将看到旧的或新的数据。Microsoft 的集合接口都没有提供任何此类保证,也没有提供任何方法让集合可以指示它可以或不能做出什么保证。

我倾向于使用 use T[],或者定义 an ,IThreadSafeList<T> where T:class其中将包含CompareExchangeItem允许Interlocked.CompareExchange在项目上使用的成员,但不会包含Insert无法Remove以线程安全方式完成的内容。