C# IEnumerable<T> 作为返回类型

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/381208/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 01:20:24  来源:igfitidea点击:

IEnumerable<T> as return type

c#collectionsienumerableenumeration

提问by Valentin Vasilyev

Is there a problem with using IEnumerable<T>as a return type? FxCop complains about returning List<T>(it advises returning Collection<T>instead).

IEnumerable<T>用作返回类型有问题吗?FxCop 抱怨返回List<T>(它建议返回Collection<T>)。

Well, I've always been guided by a rule "accept the least you can, but return the maximum."

嗯,我一直遵循一条规则“接受最少的,但返回最多的”。

From this point of view, returning IEnumerable<T>is a bad thing, but what should I do when I want to use "lazy retrieval"? Also, the yieldkeyword is such a goodie.

从这个角度来看,返回IEnumerable<T>是一件坏事,但是当我想使用“懒惰检索”时该怎么办?此外,yield关键字是一个好东西。

采纳答案by JaredPar

This is really a two part question.

这真的是一个两部分的问题。

1) Is there inherently anything wrong with returning an IEnumerable<T>

1) 返回 IEnumerable<T> 本质上有什么问题吗

No nothing at all. In fact if you are using C# iterators this is the expected behavior. Converting it to a List<T> or another collection class pre-emptively is not a good idea. Doing so is making an assumption on the usage pattern by your caller. I find it's not a good idea to assume anything about the caller. They may have good reasons why they want an IEnumerable<T>. Perhaps they want to convert it to a completely different collection hierarchy (in which case a conversion to List is wasted).

什么都没有。事实上,如果您使用 C# 迭代器,这就是预期的行为。先发制人地将其转换为 List<T> 或其他集合类并不是一个好主意。这样做是对调用者的使用模式做出假设。我发现对来电者的任何假设都不是一个好主意。他们可能有充分的理由想要 IEnumerable<T>。也许他们想将其转换为完全不同的集合层次结构(在这种情况下,转换为 List 就浪费了)。

2) Are there any circumstances where it may be preferable to return something other than IEnumerable<T>?

2) 是否有任何情况下最好返回 IEnumerable<T> 以外的内容?

Yes. While it's not a great idea to assume much about your callers, it's perfectly okay to make decisions based on your own behavior. Imagine a scenario where you had a multi-threaded object which was queueing up requests into an object that was constantly being updated. In this case returning a raw IEnumerable<T> is irresponsible. As soon as the collection is modified the enumerable is invalidated and will cause an execption to occur. Instead you could take a snapshot of the structure and return that value. Say in a List<T> form. In this case I would just return the object as the direct structure (or interface).

是的。虽然对来电者做太多假设并不是一个好主意,但根据您自己的行为做出决定是完全可以的。想象一个场景,您有一个多线程对象,它将请求排队到一个不断更新的对象中。在这种情况下,返回原始 IEnumerable<T> 是不负责任的。一旦集合被修改,枚举就会失效,并将导致执行。相反,您可以拍摄结构的快照并返回该值。以 List<T> 形式说。在这种情况下,我只会将对象作为直接结构(或接口)返回。

This is certainly the rarer case though.

不过,这当然是罕见的情况。

回答by Marc Gravell

No, IEnumerable<T>is a goodthing to return here, since all you are promising is "a sequence of (typed) values". Ideal for LINQ etc, and perfectly usable.

不,IEnumerable<T>是一个很好的事情回到这里,因为你是有希望的是“(类型)值序列”。非常适合 LINQ 等,并且完全可用。

The caller can easily put this data into a list (or whatever) - especially with LINQ (ToList, ToArray, etc).

调用者可以很容易地把这个数据到一个列表(或其他) -特别是与LINQ( ,ToListToArray等)。

This approach allows you to lazily spool back values, rather than having to buffer all the data. Definitely a goodie. I wrote-up another useful IEnumerable<T>trickthe other day, too.

这种方法允许您懒惰地后台处理值,而不必缓冲所有数据。绝对是好东西。前几天我也写了另一个有用的IEnumerable<T>技巧

回答by Adrian Zanescu

IEnumerable is fine by me but it has some drawbacks. The client has to enumerate to get the results. It has no way to check for Count etc. List is bad because you expose too much control; the client can add/remove etc. from it and that can be a bad thing. Collection seems the best compromomise, at least in FxCop's view. I allways use what seems appropiate in my context (eg. if i want to return a read only collection i expose collection as return type and return List.AsReadOnly() or IEnumerable for lazy evaluation through yield etc.). Take it on a case by case basis

IEnumerable 对我来说很好,但它有一些缺点。客户端必须枚举才能获得结果。它没有办法检查 Count 等。List 不好,因为你暴露了太多的控制;客户端可以从中添加/删除等,这可能是一件坏事。收集似乎是最好的妥协,至少在 FxCop 看来。我总是使用在我的上下文中看起来合适的东西(例如,如果我想返回一个只读集合,我将集合公开为返回类型并返回 List.AsReadOnly() 或 IEnumerable 以通过 yield 等进行延迟评估)。视具体情况而定

回答by Yuliy

Just because you say you're returning IEnumerable doesn't mean you can't return a List. The idea is to reduce unneeded coupling. All that the caller should care about is getting a list of things, rather than the exact type of collection used to contain that list. If you have something that's backed by an array, then getting something like Count is going to be fast anyway.

仅仅因为您说您要返回 IEnumerable 并不意味着您不能返回 List。这个想法是为了减少不必要的耦合。调用者应该关心的只是获取事物列表,而不是用于包含该列表的确切集合类型。如果您有一些由数组支持的东西,那么无论如何获得 Count 之类的东西都会很快。

回答by Coderer

I think your own guidance is great -- if you are able to be more specific about what you're returning without a performance hit (you don't have to e.g. build a List out of your result), do so. But if your function legitimately doesn't know what type it's going to find, like if in some situations you'll be working with a List and in some with an Array, etc., then returning IEnumerable is the "best" you can do. Think of it as the "greatest common multiple" of everything you might want to return.

我认为您自己的指导很棒——如果您能够在不影响性能的情况下更具体地说明您要返回的内容(例如,您不必从结果中构建一个列表),请这样做。但是,如果您的函数合法地不知道它将找到什么类型,例如在某些情况下您将使用 List 而在某些情况下使用 Array 等,那么返回 IEnumerable 是您可以做的“最佳” . 将其视为您可能想要返回的所有内容的“最大公倍数”。

回答by Joe

Returning IEnumerable<T> is OK if you're genuinely only returning an enumeration, and it will be consumed by your caller as such.

如果您真的只是返回一个枚举,则返回 IEnumerable<T> 是可以的,并且您的调用者将使用它。

But as others point out, it has the drawback that the caller may need to enumerate if he needs any other info (for example Count). The .NET 3.5 extension method IEnumerable<T>.Count will enumerate behind the scenes if the return value does not implement ICollection<T>, which may be undesirable.

但正如其他人指出的那样,它的缺点是调用者可能需要枚举他是否需要任何其他信息(例如 Count)。如果返回值没有实现 ICollection<T>,.NET 3.5 扩展方法 IEnumerable<T>.Count 将在幕后枚举,这可能是不可取的。

I often return IList<T> or ICollection<T> when the result is a collection - internally your method can use a List<T> and either return it as-is, or return List<T>.AsReadOnly if you want to protect against modification (e.g. if you're caching the list internally). AFAIK FxCop is quite happy with either of these.

当结果是集合时,我经常返回 IList<T> 或 ICollection<T> - 在内部,您的方法可以使用 List<T> 并按原样返回它,或者如果您想保护,则返回 List<T>.AsReadOnly反对修改(例如,如果您在内部缓存列表)。AFAIK FxCop 对其中任何一个都非常满意。

回答by Daniel Earwicker

About your principle: "accept the least you can, but return the maximum".

关于你的原则:“接受最少的,但回报最大的”。

The key to managing the complexity of a large program is a technique called information hiding. If your method works by building a List<T>, it's not often necessary to reveal this fact by returning that type. If you do, then your callers may modify the list they get back. This removes your ability to do caching, or lazy iteration with yield return.

管理大型程序复杂性的关键是一种称为信息隐藏的技术。如果您的方法通过构建 a 起作用List<T>,则通常不需要通过返回该类型来揭示这一事实。如果您这样做,那么您的呼叫者可能会修改他们返回的列表。这将消除您进行缓存或延迟迭代的能力yield return

So a better principle is for a function to follow is: "reveal as little as possible about how you work".

因此,函数要遵循的一个更好的原则是:“尽可能少地透露您的工作方式”。

回答by Amir Rezaei

One important aspect is that when you return a List<T>you are actual returning a reference. That makes it possible for a caller to manipulate your list. This is a common problem—for instance, a Business layer that returns a List<T>to a GUI layer.

一个重要的方面是,当您返回 a 时,List<T>您实际上是在返回一个reference。这使得呼叫者可以操纵您的列表。这是一个常见问题——例如,一个业务层List<T>向 GUI 层返回 a 。

回答by Indranil Bandyopadhyay

"accept the least you can, but return the maximum" is what I advocate. When a method returns an object, what justifications we have to not return the actual type and limit the capabilities of the object by returning a base type. This however raises a question how do we know what the "maximum" (actual type) will be when we design an interface. The answer is very simple. Only in extreme cases where the interface designer is designing an open interface, which will be implemented outside the application/component, they would not know what the actual return type may be. A smart designer should always consider what the method should be doing and what an optimal/generic return type should be.

“接受最少,但回报最大”是我的主张。当一个方法返回一个对象时,我们有什么理由不返回实际类型并通过返回基类型来限制对象的功能。然而,这提出了一个问题,当我们设计一个界面时,我们如何知道“最大”(实际类型)是什么。答案很简单。只有在接口设计者正在设计一个开放接口的极端情况下,该接口将在应用程序/组件之外实现,他们才会知道实际的返回类型可能是什么。聪明的设计师应该始终考虑方法应该做什么以及最佳/通用返回类型应该是什么。

E.g. If I am designing an interface to retrieve a vector of objects, and I know the count of returned objects are going to be variable, I'll always assume a smart developer will always use a List. If someone plans to return an Array, I'd question his capabilities, unless he/she is just returning the data from another layer that he/she doesn't own. And this is probably why FxCop advocates for ICollection (common base for List and Array).

例如,如果我正在设计一个接口来检索对象向量,并且我知道返回对象的数量将是可变的,我将始终假设聪明的开发人员将始终使用 List。如果有人计划返回一个数组,我会质疑他的能力,除非他/她只是从他/她不拥有的另一层返回数据。这可能就是 FxCop 提倡 ICollection(列表和数组的共同基础)的原因。

The above being said, there are couple of other things to consider

综上所述,还有一些其他的事情需要考虑

  • if the returned data should be mutable or immutable

  • if the returned data be shared across multiple callers

  • 返回的数据是否应该是可变的或不可变的

  • 如果返回的数据在多个调用者之间共享

Regarding the LINQ lazy evaluations I am sure 95%+ C# users don't understand the intestacies. It's so non-oo-ish. OO promotes concrete state changes on method invocations. LINQ lazy evaluation promotes runtime state changes on expression evaluation pattern (not something non-advanced users always follow).

关于 LINQ 惰性评估,我确信 95% 以上的 C# 用户不理解无遗症。这太不像话了。OO 促进方法调用的具体状态更改。LINQ 惰性求值促进了表达式求值模式的运行时状态变化(非高级用户总是遵循)。

回答by csaam

I can't accept the chosen answer. There are ways of dealing with the scenario described but using a List or whatever else your using isn't one of them. The moment the IEnumerable is returned you have to assume that the caller might do a foreach. In that case it doesn't matter if the concrete type is List or spaghetti. In fact just indexing is a problem especially if items are removed.

我不能接受选择的答案。有多种方法可以处理所描述的场景,但使用 List 或您使用的其他任何方法都不是其中之一。返回 IEnumerable 的那一刻,您必须假设调用者可能会执行 foreach。在这种情况下,具体类型是 List 还是 spaghetti 都没有关系。事实上,索引是一个问题,特别是如果项目被删除。

Any returned value is a snapshot. It may be the current contents of the IEnumerable in which case if it's cached it should be a clone of the cached copy; if it's supposed to be more dynamic (like the resuts of a sql query) then use yield return; however allowing the container to mutate at will and supplying methods like Count and indexer is a recipe for disaster in a multithreaded world. I haven't even gotten into the ability of the caller to call Add or Delete on a container your code is supposed to be in control of.

任何返回值都是快照。它可能是 IEnumerable 的当前内容,在这种情况下,如果它被缓存,它应该是缓存副本的克隆;如果它应该更动态(如 sql 查询的结果),则使用 yield return;然而,允许容器随意变化并提供诸如 Count 和 indexer 之类的方法是多线程世界中灾难的秘诀。我什至还没有了解调用者在您的代码应该控制的容器上调用 Add 或 Delete 的能力。

Also returning a concrete type locks you into an implementation. Today internally you may be using a list. Tomorrow maybe you do become multithreaded and want to use a thread safe container or an array or a queue or the Values collection of a dictionary or the output of a Linq query. If you lock yourself into a concrete return type then you have to either change a bunch of code or do a conversions before returning.

还返回一个具体类型会将您锁定到一个实现中。今天在内部你可能会使用一个列表。明天也许你会变成多线程并且想要使用线程安全容器或数组或队列或字典的 Values 集合或 Linq 查询的输出。如果您将自己锁定在一个具体的返回类型中,那么您必须在返回之前更改一堆代码或进行转换。