Pio*_*ula 1 ienumerable interface
谁能给我解释一下为什么我们不断地使用IEnumerable
遍布C#的时候,我的逻辑,我们应该使用List
,IList
或ICollection
代替。
好的,数字1)IEnumarable仅应用于只读集合!
太好了,每个人都在重复这一点(我明白了),所以为什么我需要不断地ToList()
对每个数据库进行调用,以防止在原始线程处理上下文时多线程陷入僵局。
当然,一旦查询被枚举为列表,我们应该从那里开始使用List以避免混淆,线程安全问题ToList()
,并一遍又一遍地不断调用每个模型。
同样在解析JSON(已枚举对象的文本表示形式)的WebAPI上,我们再次将其存储为IEnumarable。我们已经知道大小了,我们对延迟加载不感兴趣,我们可能会沿行修改该对象,所以为什么不将其存储为List of。
- 编辑
这个问题的一个重要方面是,我从不知道协方差和逆方差
List<IItem>
由于List实现上的运行时问题,您无法执行此操作,因此必须使用IEnumarable<IItem>
-仅在特定情况下有意义,即在两个不同系统上具有相同实体且需要使用一段代码的情况。否则,我List<>
自问这个问题以来一直在使用,那大概90%的时间正确用于业务层。数据库/存储库层现在坚持使用IColletion或IQueryable。
很难回答这个问题,因为它很容易发表意见,而不是回答。我会尽力的。
这是关于责任的基本问题,更具体地说,是关于良好API设计的问题。
这是我对涵盖的API设计领域的特定观点的看法:
我会举几个例子。如果您的方法可以接受任何集合,请接受IEnumerable<T>
。如果您的方法需要可索引的集合,请采用IList<T>
或类似方法。如果该方法所做的第一件事是通过将输入转换为List .ToList()
,请对其进行打开。如果我已经在外面有一个清单,并且想将其提供给您的方法,请采用List<T>
。
但是,我必须意识到一个事实,一旦您说了算IList<T>
,便可以修改列表。如果可以,请直接发送清单。否则,我将不得不.ToList()
在外面执行。这并不比以前更糟,这只是一个有关谁负责的问题。
相反,如果您的方法返回的对象是a List<T>
,则将其返回为List<T>
或IList<T>
。不要把它藏在后面IEnumerable<T>
。
在这里,您还应该知道,任何取回此列表的调用者都可以对其进行修改。如果这样不行,请不要将其返回为IList<T>
。
不过,您可以在最后一个示例中说,如果该列表不应该在外部进行修改,则在返回后(不是调用者列表),则应将其隐藏在后面IEnumerable<T>
,但这仅是隐藏事实,程序员可以仍将其投射回IList<T>
并对其进行修改。如果您不能接受,请通过发送副本.ToList()
。
但是,所有这些都归结为责任。
如果您的方法返回了IEnumerable<T>
,不是我,正在编写使用该方法的代码的人,是否依赖于您的方法正常工作?如果您的方法由于已经处理了数据库上下文而无法使用,则取决于您,您的代码,错误,问题。
同样,如果我需要可以索引的内容,但除此之外,它是一个纯只读集合,则应设计我的方法以采用适当的集合接口来发出信号。如果设计要采用的方法IEnumerable<T>
,我应该知道我是在专门说“我将采用任何懒惰产生的集合”。如果我不知道或不想这么说,那我就不要接受IEnumerable<T>
。
以便为您的特定代码行回答基本问题。在这里,您绝对必须.ToList()
在内部调用,因为否则您的方法将损坏,出错或出错。
或,您可以重新设计您的方法,使其保留其惰性特性,但在就绪之前不处理数据库上下文。在这方面,您对方法的简单更改将是使用yield return
:
using (var context = ... )
{
foreach (var element in context.Request(...))
yield return element;
}
Run Code Online (Sandbox Code Playgroud)
进行这样的更改后,您仍然要推迟访问数据库的实际成本,直到调用者实际遍历返回的集合,并且仍在正确处理上下文(假定调用者正确进行了迭代)。在这里,我仍然IEnumerable<T>
要特别指出“我被懒洋洋地评价了”。
但是,如果没有保留您在内部产生的列表,则没有人对此负责,您实际上是在将此列表的责任交给调用者,那么绝对应该将其返回为List<T>
。不要把它藏在后面IEnumerable<T>
。