简体   繁体   English

我应该总是返回 IEnumerable<t> 而不是 IList<t> ?</t></t>

[英]Should I always return IEnumerable<T> instead of IList<T>?

When I'm writing my DAL or other code that returns a set of items, should I always make my return statement:当我编写我的 DAL 或其他返回一组项目的代码时,我是否应该始终做出我的返回声明:

public IEnumerable<FooBar> GetRecentItems()

or要么

public IList<FooBar> GetRecentItems()

Currently, in my code I have been trying to use IEnumerable as much as possible but I'm not sure if this is best practice?目前,在我的代码中我一直在尝试尽可能多地使用 IEnumerable 但我不确定这是否是最佳实践? It seemed right because I was returning the most generic datatype while still being descriptive of what it does, but perhaps this isn't correct to do.这似乎是正确的,因为我返回了最通用的数据类型,同时仍然描述了它的作用,但也许这样做是不正确的。

Framework design guidelines recommend using the class Collection when you need to return a collection that is modifiable by the caller or ReadOnlyCollection for read only collections.当您需要返回可由调用者修改的集合或只读集合的ReadOnlyCollection 时,框架设计指南建议使用类Collection

The reason this is preferred to a simple IList is that IList does not inform the caller if its read only or not.这比简单的IListIList的原因是IList不会通知调用者它是否为只读。

If you return an IEnumerable<T> instead, certain operations may be a little trickier for the caller to perform.如果您改为返回IEnumerable<T> ,则调用者执行某些操作可能会有些棘手。 Also you no longer will give the caller the flexibility to modify the collection, something that you may or may not want.此外,您将不再为调用者提供修改集合的灵活性,这是您可能想要也可能不想要的。

Keep in mind that LINQ contains a few tricks up its sleeve and will optimize certain calls based on the type they are performed on.请记住,LINQ 包含一些技巧,并会根据执行的类型优化某些调用。 So, for example, if you perform a Count and the underlying collection is a List it will NOT walk through all the elements.因此,例如,如果您执行Count并且底层集合是 List,则它不会遍历所有元素。

Personally, for an ORM I would probably stick with Collection<T> as my return value.就我个人而言,对于 ORM,我可能会坚持使用Collection<T>作为我的返回值。

It really depends on why you are using that specific interface.这实际上取决于您使用该特定界面的原因。

For example, IList<T> has several methods that aren't present in IEnumerable<T> :例如, IList<T>有几个方法在IEnumerable<T>中不存在:

  • IndexOf(T item)
  • Insert(int index, T item)
  • RemoveAt(int index)

and Properties:和属性:

  • T this[int index] { get; set; }

If you need these methods in any way, then by all means return IList<T> .如果您以任何方式需要这些方法,那么一定要返回IList<T>

Also, if the method that consumes your IEnumerable<T> result is expecting an IList<T> , it will save the CLR from considering any conversions required, thus optimizing the compiled code.此外,如果使用IEnumerable<T>结果的方法需要IList<T> ,它将使 CLR 免于考虑任何所需的转换,从而优化已编译的代码。

In general, you should require the most generic and return the most specific thing that you can.通常,您应该要求最通用的并返回最具体的东西。 So if you have a method that takes a parameter, and you only really need what's available in IEnumerable, then that should be your parameter type.因此,如果您有一个接受参数的方法,并且您只需要 IEnumerable 中可用的内容,那么这应该是您的参数类型。 If your method could return either an IList or an IEnumerable, prefer returning IList.如果您的方法可以返回 IList 或 IEnumerable,则更喜欢返回 IList。 This ensures that it is usable by the widest range of consumers.这确保它可供最广泛的消费者使用。

Be loose in what you require, and explicit in what you provide.在你需要的东西上松散,在你提供的东西上明确。

That depends...那要看...

Returning the least derived type ( IEnumerable ) will leave you the most leeway to change the underlying implementation down the track.返回最少派生类型( IEnumerable )将为您留下最大的余地来更改底层实现。

Returning a more derived type ( IList ) provides the users of your API with more operations on the result.返回一个更派生的类型 ( IList ) 为您的 API 用户提供了对结果的更多操作。

I would always suggest returning the least derived type that has all the operations your users are going to need... so basically, you first have to deremine what operations on the result make sense in the context of the API you are defining.我总是建议返回具有您的用户将需要的所有操作的最少派生类型......所以基本上,您首先必须确定在您定义的 API 的上下文中对结果进行的哪些操作是有意义的。

One thing to consider is that if you're using a deferred-execution LINQ statement to generate your IEnumerable<T> , calling .ToList() before you return from your method means that your items may be iterated twice - once to create the List, and once when the caller loops through, filters, or transforms your return value.需要考虑的一件事是,如果您使用延迟执行 LINQ 语句来生成IEnumerable<T> ,则在从方法返回之前调用.ToList()意味着您的项目可能会迭代两次 - 一次以创建 List ,当调用者循环时,过滤或转换您的返回值一次。 When practical, I like to avoid converting the results of LINQ-to-Objects to a concrete List or Dictionary until I have to.在可行的情况下,我喜欢避免将 LINQ-to-Objects 的结果转换为具体的列表或字典,除非我必须这样做。 If my caller needs a List, that's a single easy method call away - I don't need to make that decision for them, and that makes my code slightly more efficient in the cases where the caller is just doing a foreach.如果我的调用者需要一个 List,那么这是一个简单的方法调用——我不需要为他们做出那个决定,这使得我的代码在调用者只是执行 foreach 的情况下效率更高。

List<T> offers the calling code many more features, such as modifying the returned object and access by index. List<T>为调用代码提供了更多功能,例如修改返回的对象和通过索引访问。 So the question boils down to: in your application's specific use case, do you WANT to support such uses (presumably by returning a freshly constructed collection!), for the caller's convenience -- or do you want speed for the simple case when all the caller needs is to loop through the collection and you can safely return a reference to a real underlying collection without fearing this will get it erroneously changed, etc?所以问题归结为:在您的应用程序的特定用例中,您是否想要支持此类用途(大概是通过返回一个新构造的集合!),为了调用者的方便 - 或者您是否想要在所有情况下的简单情况下的速度调用者需要循环遍历集合,您可以安全地返回对真正底层集合的引用,而不必担心这会导致它被错误地更改,等等?

Only you can answer this question, and only by understanding well what your callers will want to do with the return value, and how important performance is here (how big are the collections you would be copying, how likely is this to be a bottleneck, etc).只有您才能回答这个问题,并且只有通过充分了解您的调用者想要对返回值做什么,以及性能在这里有多重要(您将复制的集合有多大,这有多大可能成为瓶颈,等等)。

It's not so simple when you are talking about return values instead of input parameters.当您谈论返回值而不是输入参数时,这并不是那么简单。 When it's an input parameter, you know exactly what you need to do.当它是一个输入参数时,您确切地知道您需要做什么。 So, if you need to be able to iterate over the collection, you take an IEnumberable whereas if you need to add or remove, you take an IList.因此,如果您需要能够遍历集合,则使用 IEnumberable,而如果您需要添加或删除,则使用 IList。

In the case of a return value, it's tougher.在返回值的情况下,它更难。 What does your caller expect?您的来电者期望什么? If you return an IEnumerable, then he will not know a priori that he can make an IList out of it.如果您返回一个 IEnumerable,那么他将不会先验地知道他可以从中制作一个 IList。 But, if you return an IList, he will know that he can iterate over it.但是,如果你返回一个 IList,他就会知道他可以迭代它。 So, you have to take into account what your caller is going to do with the data.因此,您必须考虑您的调用者将如何处理数据。 The functionality that your caller needs/expects is what should govern when making the decision on what to return.您的调用者需要/期望的功能是在决定返回什么时应该支配的。

I think you can use either, but each has a use.我认为你可以使用任何一个,但每个都有一个用途。 Basically List is IEnumerable but you have count functionality, add element, remove element基本上ListIEnumerable但你有计数功能,添加元素,删除元素

IEnumerable is not efficient for counting elements IEnumerable 对于元素计数效率不高

If the collection is intended to be readonly, or the modification of the collection is controlled by the Parent then returning an IList just for Count is not a good idea.如果集合是只读的,或者集合的修改由Parent控制,那么只为Count返回一个IList不是一个好主意。

In Linq, there is a Count() extension method on IEnumerable<T> which inside the CLR will shortcut to .Count if the underlying type is of IList , so performance difference is negligible.在 Linq 中, IEnumerable<T>上有一个Count()扩展方法,如果基础类型是IList ,它在 CLR 内部将快捷方式到.Count ,因此性能差异可以忽略不计。

Generally I feel (opinion) it is better practice to return IEnumerable where possible, if you need to do additions then add these methods to the parent class, otherwise the consumer is then managing the collection within Model which violates the principles, eg manufacturer.Models.Add(model) violates law of demeter.一般来说,我觉得(意见)最好在可能的情况下返回 IEnumerable,如果您需要添加这些方法,然后将这些方法添加到父类,否则消费者将在 Model 中管理违反原则的集合,例如manufacturer.Models.Add(model)违反了德米特法则。 Of course these are just guidelines and not hard and fast rules, but until you have full grasps of applicability, following blindly is better than not following at all.当然,这些只是指导方针,并不是硬性规定,但在你完全掌握适用性之前,盲目遵循总比不遵循要好。

public interface IManufacturer 
{
     IEnumerable<Model> Models {get;}
     void AddModel(Model model);
}

(Note: If using nNHibernate you might need to map to private IList using different accessors.) (注意:如果使用 nNHibernate,您可能需要使用不同的访问器映射到私有 IList。)

TL; TL; DR;博士; – summary - 概括

  • If you develop in-house software, do use the specific type(Like List ) for the return values and the most generic type for input parameters even in case of collections.如果您开发内部软件,请务必使用特定类型(如List )作为返回值,并使用最通用的类​​型作为输入参数,即使是在集合的情况下。
  • If a method is a part of a redistributable library's public API, use interfaces instead of concrete collection types to introduce both return values and input parameters.如果方法是可再发行库的公共 API 的一部分,请使用接口而不是具体的集合类型来引入返回值和输入参数。
  • If a method returns a read-only collection, show that by using IReadOnlyList or IReadOnlyCollection as the return value type.如果方法返回只读集合,请使用IReadOnlyListIReadOnlyCollection作为返回值类型来显示。

More 更多的

I think you can use either, but each has a use.我认为你可以使用任何一个,但每个都有一个用途。 Basically List is IEnumerable but you have count functionality, Add element, remove element基本上ListIEnumerable但你有计数功能,添加元素,删除元素

IEnumerable is not efficient for counting elements, or getting a specific element in the collection. IEnumerable在计算元素或获取集合中的特定元素时效率不高。

List is a collection which is ideally suited to finding specific elements, easy to add elements, or remove them. List是一个非常适合查找特定元素、易于添加或删除元素的集合。

Generally I try to use List where possible as this gives me more flexibility.一般来说,我尽量使用List ,因为这给了我更大的灵活性。

Use List<FooBar> getRecentItems() rather than IList<FooBar> GetRecentItems()使用List<FooBar> getRecentItems()而不是IList<FooBar> GetRecentItems()

as all have said it depends, if you don't want Add/Remove functioanlity at calling layer then i will vote for IEnumerable as it provides only iteration and basic functionality which in design prespective i like.正如所有人所说,这取决于,如果您不希望在调用层添加/删除功能,那么我将投票支持 IEnumerable,因为它仅提供我喜欢的迭代和基本功能。 Returning IList my votes are always againist it but it's mainly what you like and what not.返回 IList 我的投票总是反对它,但这主要是你喜欢什么,不喜欢什么。 in performance terms i think they are more of same.在性能方面,我认为它们更相似。

If you do not counting in your external code it is always better to return IEnumerable, because later you can change your implementation (without external code impact), for example, for yield iterator logic and conserve memory resources (very good language feature by the way).如果您不计入外部代码,返回 IEnumerable 总是更好,因为稍后您可以更改您的实现(不受外部代码影响),例如,用于yield 迭代器逻辑并节省内存资源(顺便说一句,非常好的语言功能)。

However if you need items count, don't forget that there is another layer between IEnumerable and IList - ICollection .但是,如果您需要项目计数,请不要忘记 IEnumerable 和 IList - ICollection之间还有另一层。

I might be a bit off here, seeing that no one else suggested it so far, but why don't you return an (I)Collection<T> ?我可能有点不对劲,看到到目前为止没有其他人建议它,但你为什么不返回(I)Collection<T>

From what I remember, Collection<T> was the preferred return type over List<T> because it abstracts away the implementation.根据我的记忆, Collection<T>是比List<T>首选的返回类型,因为它抽象了实现。 They all implement IEnumerable , but that sounds to me a bit too low-level for the job.他们都实现了IEnumerable ,但这对我来说听起来有点太低级了。

I think the general rule is to use the more specific class to return, to avoid doing unneeded work and give your caller more options.我认为一般规则是使用更具体的类来返回,以避免做不必要的工作并给你的调用者更多的选择。

That said, I think it's more important to consider the code in front of you which you are writing than the code the next guy will write (within reason.) This is because you can make assumptions about the code that already exists.就是说,我认为考虑您正在编写的代码比考虑下一个人将编写的代码(在合理范围内)更重要。这是因为您可以对已经存在的代码进行假设。

Remember that moving UP to a collection from IEnumerable in an interface will work, moving down to IEnumerable from a collection will break existing code.请记住,在界面中从 IEnumerable 向上移动到集合会起作用,从集合向下移动到 IEnumerable 将破坏现有代码。

If these opinions all seem conflicted, it's because the decision is subjective.如果这些意见看起来都相互矛盾,那是因为这个决定是主观的。

IEnumerable<T> contains a small subset of what is inside List<T> , which contains the same stuff as IEnumerable<T> but more! IEnumerable<T>包含List<T>内部内容的一小部分,其中包含与IEnumerable<T>相同的内容,但更多! You only use IEnumerable<T> if you want a smaller set of features.如果您想要一组较小的功能,则只能使用IEnumerable<T> Use List<T> if you plan to use a larger, richer set of features.如果您计划使用更大、更丰富的功能集,请使用List<T>

The Pizza Explanation披萨的解释

Here is a much more comprehensive explanation of why you would use an Interface like IEnumerable<T> versus List<T> , or vise versa, when instantiating objects in C languages like Microsoft C#.这里有一个更全面的解释,说明在使用 C 语言(如 Microsoft C#)实例化对象时,为什么要使用IEnumerable<T>List<T>之类的接口,反之亦然。

Think of Interfaces like IEnumerable<T> and IList<T> as the individual ingredients in a pizza (pepperoni, mushrooms, black olives...) and concrete classes like List<T> as the pizza .IEnumerable<T>IList<T>等接口视为比萨饼(意大利辣香肠、蘑菇、黑橄榄...)中的各种成分,将List<T>具体类视为比萨饼 List<T> is in fact a Supreme Pizza that always contains all the Interface ingredients combined (ICollection, IEnumerable, IList, etc). List<T>实际上是一个Supreme Pizza ,它始终包含组合的所有接口成分(ICollection、IEnumerable、IList 等)。

What you get as far as a pizza and its toppings is determined by how you "type" your list when you create its object reference in memory. You have to declare the type of pizza you are cooking as follows:你得到的比萨饼及其配料取决于你在 memory 中创建其 object 引用时如何“键入”列表。你必须声明你正在烹饪的比萨饼类型,如下所示:

// Pepperoni Pizza: This gives you a single Interface's members,
// or a pizza with one topping because List<T> is limited to 
// acting like an IEnumerable<T> type.

IEnumerable<string> pepperoniPizza = new List<string>();


// Supreme Pizza: This gives you access to ALL 8 Interface
// members combined or a pizza with ALL the ingredients
// because List type uses all Interfaces!!

IList<string> supremePizza = new List<string>();

Note you cannot instantiate an Interface as itself (or eat raw pepperoni).请注意,您不能将接口实例化为它本身(或吃生意大利辣香肠)。 When you instantiate List<T> as one Interface type like IEnumerable<T> you only have access to its Implementations and get the pepperoni pizza with one topping.当您将List<T>实例化为IEnumerable<T>之类的一种接口类型时,您只能访问其实现并获得带有一种浇头的意大利辣香肠披萨。 You can only access IEnumerable<T> members and cannot see all the other Interface members in List<T> .您只能访问IEnumerable<T>成员,而看不到List<T>中的所有其他接口成员。

When List<T> is instantiated as IList<T> it implements all 8 Interfaces, so it has access to all the members of all the Interfaces it has implemented (or a Supreme Pizza toppings)!List<T>实例化为IList<T>时,它实现了所有 8 个接口,因此它可以访问它已实现的所有接口的所有成员(或 Supreme Pizza 浇头)!

Here is the List<T> class, showing you WHY that is.这是List<T> class,向您展示了为什么会这样。 Notice the List<T> in the .NET Library has implemented all the other Interfaces including IList!!请注意 .NET 库中的List<T>已实现所有其他接口,包括 IList!! But IEnumerable<T> implements just a small subsection of those List Interface members.但是IEnumerable<T>只实现了这些列表接口成员的一小部分。

public class List<T> :

    ICollection<T>,
    IEnumerable<T>,
    IEnumerable,
    IList<T>,
    IReadOnlyCollection<T>,
    IReadOnlyList<T>,
    ICollection,
    IList

{
// List<T> types implement all these goodies and more!
public List();
public List(IEnumerable<T> collection);
public List(int capacity);
public T this[int index] { get; set; }
public int Count { get; }
public int Capacity { get; set; }
public void Add(T item);
public void AddRange(IEnumerable<T> collection);
public ReadOnlyCollection<T> AsReadOnly();
public bool Exists(Predicate<T> match);
public T Find(Predicate<T> match);
public void ForEach(Action<T> action);
public void RemoveAt(int index);
public void Sort(Comparison<T> comparison);

// ......and much more....

}

So why NOT instantiate List<T> as List<T> ALL THE TIME?那么为什么不一直将List<T>实例化为List<T>呢?

Instantiating a List<T> as List<T> gives you access to all Interface members!List<T>实例化为List<T>可以让您访问所有接口成员! But you might not need everything.但你可能不需要一切。 Choosing one Interface type allows your application to store a smaller object with less members and keeps your application tight.选择一种接口类型可以让您的应用程序存储更小的 object 和更少的成员,并使您的应用程序保持紧凑。 Who needs Supreme Pizza every time?谁每次都需要Supreme Pizza

But there is a second reason for using Interface types: Flexibility .但是使用接口类型还有第二个原因:灵活性 Because other types in .NET, including your own custom ones, might use the same "popular" Interface type, it means you can later substitute your List<T> type with any other type that implements, say IEnumerable<T> .因为 .NET 中的其他类型(包括您自己的自定义类型)可能会使用相同的“流行”接口类型,这意味着您稍后可以将List<T>类型替换为任何其他实现的类型,例如IEnumerable<T> If your variable is an Interface type, you can now switch out the object created with something other than List<T> .如果您的变量是接口类型,您现在可以切换出使用List<T>以外的其他内容创建的 object。 Dependency Injection is a good example of this type of flexibility using Interfaces rather than concrete types, and why you might want to create objects using Interfaces.依赖注入是这种使用接口而不是具体类型的灵活性的一个很好的例子,以及为什么你可能想要使用接口创建对象。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 为什么IEnumerable <T> .ToList <T>()返回List <T>而不是IList <T>? - Why does IEnumerable<T>.ToList<T>() return List<T> instead of IList<T>? IList <T>,IEnumerable <T>和ObservableCollection <T> - IList<T>, IEnumerable<T> and ObservableCollection<T> 为什么我不能通过 IList<childtype> 到 F(IEnumerable<parenttype> )?</parenttype></childtype> - Why can't I pass IList<ChildType> to F(IEnumerable<ParentType>)? 为什么选择列表<T>继承 IEnumerable<T> 和 IEnumerable 再次 - Why IList<T> inherits IEnumerable<T> and IEnumerable again IList的 <T> vs IEnumerable <T> 。 什么是更有效的IList <T> 或IEnumerable <T> - IList<T> vs IEnumerable<T>. What is more efficient IList<T> or IEnumerable<T> 如何返回 IEnumerable<T> /IList<T> 为空或使用空合并运算符的值 - How return IEnumerable<T>/IList<T> is null or value using null-coalescing operator 在IList.IndexOf()之类的东西,但在IEnumerable <T>? - Something in the likes of IList.IndexOf() but on IEnumerable<T>? 我应该使用什么方法作为IEnumerable,IList,Collection或方法的返回类型 - What should I use as return type of method IEnumerable, IList, Collection or what 我可以(或应该)使用 IAsyncEnumerable<T> 而不是任务<ActionResult<IEnumerable<T> &gt;&gt; 在 Web API 控制器中 - Can (or should) I use IAsyncEnumerable<T> instead of Task<ActionResult<IEnumerable<T>>> in a Web API Controller 为什么我不应该总是使用ICollection而不是IEnumerable? - Why I should not always be using ICollection instead of IEnumerable?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM