有人可以向我解释为什么我们经常使用IEnumerable
来自C#,根据我的逻辑,我们应该使用List
,IList
或ICollection
。
好的,数字1)IEnumarable只能用于只读集合!
很好,每个人都在不断重复(我明白了),所以为什么我需要经常需要在每个数据库调用上放置ToList()
以防止在原始线程处理上下文时多线程崩溃。
当查询被枚举到List时,我们应该只使用List来避免混淆,线程安全问题,并且不断地在每个模型上调用ToList()
,一遍又一遍。
同样在解析JSON(已经枚举的对象的文本表示)的WebAPI上,我们再次将其存储为IEnumarable。我们已经知道了大小,我们对延迟加载不感兴趣,我们可能会修改该对象的某个位置,所以为什么我们不将它存储为List of。
- 编辑
关于这个问题的一个重要方面是我从未了解过covariance and contravariance
由于List实现上的运行时问题,您无法执行List<IItem>
,因此您必须使用IEnumarable<IItem>
- 这仅适用于在两个不同系统上具有相同实体的特定情况,需要处理一段代码。否则我一直在使用List<>
,因为提出问题并且可能有90%的时间在商业层中使用正确。数据库/存储库层现在坚持IColletion或IQueryable。
答案 0 :(得分:4)
有点难以回答这个问题,因为发表意见非常容易,而不是答案。我会尝试。
这是关于责任的基本问题,更具体地说,是良好的API设计。
以下是我对API设计世界特定角落的看法:
我举几个例子。如果您的方法可以进行任何收集,请执行IEnumerable<T>
。如果您的方法需要可索引的集合,请使用IList<T>
或类似方法。如果该方法所做的第一件事是通过.ToList()
将输入转换为List,那么请打开它。如果我已经在外面有一个列表,并且我想将添加到您的方法中,请执行List<T>
。
但是,我必须知道,一旦您说出IList<T>
,,您就可以修改列表。如果可以,那很好,我可以直接发给你我的清单。否则,我将不得不在外部执行.ToList()
。这并不比以前更糟糕,它只是一个关于谁负责这样做的问题。
另一方面,如果您的方法返回List<T>
的对象,请将其返回为List<T>
或IList<T>
。不要将其隐藏在IEnumerable<T>
后面。
在这里你还应该知道任何获得此列表的调用者都可以修改它。如果不是这样,请不要将其作为IList<T>
返回。
尽管如此,你可以在最后一个例子中说,如果该列表不应该在外部修改,在返回之后(它不是调用者列表),那么你应该将它隐藏在{{1}之后但是这只隐藏了这个事实,程序员仍然可以将它强制转换回IEnumerable<T>
并修改它。如果您无法接受此操作,请通过IList<T>
返回副本。
但是,所有这些都可以追溯到责任。
如果您的方法返回.ToList()
,那么编写使用此方法的代码的人依赖于正常工作的方法吗?如果您的方法因为您已经处理了数据库上下文而无法工作,那么这就是您,您的代码,您的错误,您的问题。
同样,如果我需要可以索引的东西,但除此之外是纯粹的只读集合,我应该设计我的方法来采取适当的集合接口来发出信号。如果我设计我的方法来取IEnumerable<T>
,我应该知道我特意说&#34;我将采取任何懒惰的制作集合&#34;。如果我不知道,或者不想这么说,那么我就不应该IEnumerable<T>
。
为了回答特定代码行的基本问题。在这里你绝对必须在里面调用IEnumerable<T>
,否则你的方法会被破坏,错误,错误。
或,您可以重新设计方法,使其保持其惰性,但在准备好之前不会处置数据库上下文。在这方面对您的方法进行简单的更改将是使用.ToList()
:
yield return
通过这样的更改,您仍然会推迟转到数据库的实际成本,直到调用者实际迭代您返回的集合,并且您仍然正确处理上下文(假设调用者执行了此操作)迭代正确)。在这里,我仍然会回复using (var context = ... )
{
foreach (var element in context.Request(...))
yield return element;
}
专门说&#34;我懒得评价&#34;。
但是,如果您没有保留内部生成的列表,则没有人对此负责,您有效地将此列表的责任交给调用者,那么绝对应该将其作为IEnumerable<T>
返回。不要将其隐藏在List<T>
后面。
答案 1 :(得分:0)
IEnumerable没有明确说明数据集的集合类型,因此您可以执行更多通用编程,而不是具体特定类型的函数。