我应该总是返回IEnumerable <T>而不是IList <T>?
当我正在写我的DAL或其他返回一组项目的代码时,我是否应该始终使用我的返回语句:
public IEnumerable<FooBar> GetRecentItems()
要么
public IList<FooBar> GetRecentItems()
目前,在我的代码中,我一直试图尽可能使用IEnumerable,但我不确定这是否是最佳实践? 这似乎是正确的,因为我正在返回最通用的数据types,同时仍然描述它的作用,但也许这是不正确的。
这真的取决于你为什么使用特定的接口。
例如, IList<T>
有几个在IEnumerable<T>
中不存在的方法:
-
IndexOf(T item)
-
Insert(int index, T item)
-
RemoveAt(int index)
和属性:
-
T this[int index] { get; set; }
如果您以任何方式需要这些方法,那么通过一切手段返回IList<T>
。
另外,如果消耗IEnumerable<T>
结果的方法需要IList<T>
,它将保存CLR不考虑所需的任何转换,从而优化已编译的代码。
框架devise指导方针build议在需要将调用方或ReadOnlyCollection可修改的集合返回为只读集合时使用类Collection 。
这个比单纯的IList更受欢迎的原因是IList不会告诉调用者是否只读。
如果您返回一个IEnumerable<T>
,则某些操作对于调用者来说可能会有点棘手。 你也不会再给调用者灵活地修改这个集合,这是你可能想要或不想要的。
请记住,LINQ包含一些技巧,并会根据它们执行的types优化某些调用。 所以,举例来说,如果你执行一个Count,而底层的集合是一个List,它将不会遍历所有的元素。
就个人而言,对于一个ORM我可能坚持Collection<T>
作为我的返回值。
那要看…
返回最小派生types( IEnumerable
)会给你留下最大的余地来改变底层的实现。
返回更多的派生types( IList
)为您的API的用户提供更多的结果操作。
我总是build议返回具有用户所需操作的最小派生types…因此,基本上,首先必须清除在定义的API的上下文中对结果的操作是否有意义。
一般来说,你应该要求最通用的,并返回你可以的最具体的东西。 所以,如果你有一个方法需要一个参数,而你只需要IEnumerable中的可用参数,那么这应该是你的参数types。 如果你的方法可以返回一个IList或者一个IEnumerable,那么更喜欢返回IList。 这确保了它可以被最广泛的消费者使用。
放松你所要求的,明确你所提供的。
List<T>
为调用代码提供了更多的function,例如修改返回的对象和按索引访问。 所以这个问题归结为:在你的应用程序的具体使用情况下,你是否想支持这种使用(大概是通过返回一个新build的集合!),为了方便调用者 – 或者你想要速度为简单的情况,当所有的调用者的需求是循环通过集合,你可以安全地返回一个真正的底层集合的引用,而不用担心这会导致错误的改变,等等?
只有你能够回答这个问题,而且只有通过理解你的打电话者想要做什么来回报价值,以及这里的performance有多重要(你将复制的collections有多大,这可能是一个瓶颈,等等)。
需要考虑的一件事是,如果您使用延迟执行的LINQ语句来生成IEnumerable<T>
,则在从方法返回之前调用.ToList()
意味着您的项目可能会迭代两次 – 一次创buildList ,一旦调用者循环,过滤或转换您的返回值。 实际上,我希望避免将LINQ-to-Objects的结果转换为具体的List或Dictionary,直到必须完成。 如果我的调用者需要一个List,这是一个简单的方法,我不需要为他们做出这个决定,这使得我的代码在调用者只是做一个foreach的情况下稍微有效一些。
我认为你可以使用,但每个都有用处。 基本上
List
是IEnumerable
但你有计数function,添加元素,删除元素IEnumerable对元素计数效率不高
如果这个集合是只读的,或者集合的修改是由Parent
控制的,那么返回一个IList
只是为了Count
而不是一个好主意。
在Linq中,在IEnumerable<T>
上有一个Count()
扩展方法,如果底层types是IList
,那么CLR内部将快速到.Count
,因此性能差异可以忽略不计。
一般来说,我认为(意见)最好是在可能的情况下返回IEnumerable,如果需要添加,则将这些方法添加到父类中,否则,消费者将在Model中违反原则pipe理集合,例如manufacturer.Models.Add(model)
违反demeter的法则。 当然,这些只是指导而已,并不是硬性规定,但是直到你掌握了适用性,盲目追随总比没有追随好。
public interface IManufacturer { IEnumerable<Model> Models {get;} void AddModel(Model model); }
(注意:如果使用nNHibernate,可能需要使用不同的访问器映射到私有IList。)
当你谈论返回值而不是input参数时,这并不是那么简单。 当它是一个input参数时,你确切地知道你需要做什么。 所以,如果你需要能够遍历集合,你需要一个IEnumberable,而如果你需要添加或删除,你需要一个IList。
在返回值的情况下,这是更困难的。 你的来电者期望什么? 如果你返回一个IEnumerable,那么他就不会先知道他可以让一个IList出来。 但是,如果你返回一个IList,他会知道他可以迭代它。 所以,你必须考虑你的调用者将如何处理数据。 调用者需要/期望的function是在决定返回什么时应该pipe理的。
我认为你可以使用,但每个都有用处。 基本上List
是IEnumerable
但你有计数function,添加元素,删除元素
IEnumerable
对计算元素或获取集合中的特定元素效率不高。
List
是一个集合,非常适合查找特定元素,易于添加元素或删除它们。
通常我尽可能地使用List
,因为这给了我更多的灵活性。
使用List<FooBar> getRecentItems()
而不是IList<FooBar> GetRecentItems()
正如所有人所说,这取决于,如果你不想在调用层添加/删除function,那么我将投票IEnumerable,因为它只提供了迭代和基本的function,在devise方面我喜欢。 返回IList我的选票总是再次主义,但主要是你喜欢什么,不喜欢什么。 在性能方面,我认为他们是相同的。
如果不计算外部代码,最好返回IEnumerable,因为稍后您可以更改实现(无需外部代码影响),例如,对于yield iterator逻辑并节省内存资源(非常好的语言function) )。
但是,如果您需要项目计数,请不要忘记在IEnumerable和IList之间还有另外一层 – ICollection 。
我可能会在这里看到一点,但是为什么不回到(I)Collection<T>
?
从我记得, Collection<T>
是比List<T>
优先的返回types,因为它抽象出实现。 他们都实现IEnumerable
,但这听起来对我来说太低级了。
我认为一般的规则是使用更具体的类来返回,以避免做不必要的工作,给你的来电者更多的select。
也就是说,我认为考虑你正在写的代码比下一个人写的代码更重要(在合理的范围内)。这是因为你可以对已经存在的代码进行假设。
请记住,从界面中向上移动到IEnumerable的集合将起作用,从集合向下移动到IEnumerable将破坏现有的代码。
如果这些意见看起来都是矛盾的,那是因为这个决定是主观的。