问题
In many projects I work on, whenever I have to return a read only collection, I use the IEnumerable<T>
interface and make it type specific like so:
Public ReadOnly Property GetValues() As IEnumerable(Of Integer)
Get
'code to return the values'
End Get
End Property
Most of the time, I return a List but in some functions and read only properties I return an array which also serves the purpose alright by kind courtesy of Extension Methods.
My question is am I violating any design principles by returning IEnumerable<T>
s instead of specific types (e.g.: List<T>
, HashSet<T>
, Stack<T>
or Array
s)?
回答1:
I generally prefer IEnumerable<T>
as well. The main thing is to ask yourself what actual (even minimum) functionality is being returned from the method (or passed to it, in the case of a method argument).
If all you need to do is enumerate over a result set, then IEnumerable<T>
does exactly that. No more, no less. This leaves you the flexibility to return more specific types in certain cases if need be without breaking the footprint of the method.
回答2:
David's answer pretty much covers it; IEnumerable<T>
is best practice in general. You can see this by looking at most of the newer framework methods.
I just wanted to add that, since you specified read-only collection in your original question, you can enforce this by calling .ToList().AsReadOnly()
on your IEnumerable<T>
instance before returning it. This will create a concrete instance of ReadOnlyCollection<T>
for you to return. Your return type should still be IEnumerable<T>
, since the caller doesn't need to know that you are specifically returning a ReadOnlyCollection<T>
, but in this way you prevent the caller from shooting himself in the foot.
(Without this step, the caller could e.g. attempt to cast the IEnumerable<T>
it gets from your method to List<T>
. If the cast succeeds, which it will if that's what you were originally working with, the caller can then modify the same list you are operating on inside your method, with possibly unanticipated consequences.)
回答3:
Personally I consider that returning IEnumerable<T>
from an API is a strong hint that the implementation may use lazy evaluation.
Knowing that lazy evaluation can be used may be important to the caller, as it means that:
iterating over the result more than once (e.g. to obtain a count then access the data) will result in the evaluation being done more than once.
exceptions can be thrown from the API while iterating over the result:
e.g.
IEnumerable<MyObject> LazyEvaluatedApi()
{
}
...
IEnumerable<MyObject> result = LazyEvaluatedApi();
...
foreach(MyObject item in result) // Exception from LazyEvaluatedApi may be thrown here.
{
}
If you don't think any implementation will need to use lazy evaluation (e.g. a Data Access Layer API), I would prefer to return ICollection<T>
or IList<T>
. Apart from giving the caller access to a Count
property (ICollection<T>
and IList<T>
) and an indexer (IList<T>
only), you are also clearly indicating that there will be no lazy evaluation.
When returning your ICollection<T>
or IList<T>
, your concrete implementation will probably usually return a List<T>
. If it's important that the list is readonly, then return List<T>.AsReadOnly()
回答4:
It depends on what you want to do and what you intend "violating". This depends from:
- If you want your collections returned by your objects could be altered by outside code, the you must returns modifiable types aka List<>, HashSet<> Stack<> or any else which permit modifications
- If you want your "collection user" to just iterate on collection items but without modifying the collection itself, this is the right choice
Take in mind that many good design gurus and principles prefers to return IEnumerable<> over modifiable-collection-ready
回答5:
Returning IEnumerable<T>
is fine. Although watch out that if T is a reference type it's possible for the caller to modify the object.
You should return the most general type that offers the minimum level of functionality you require. In this case, if your caller just needs to interate over the data then IEnumerable is more appropriate than List<T>
, HashSet<T>
or any other collection type. This way your caller remains uncoupled from the implementation of your method and you are free to change your methods implementation in the future without disrupting your callers.
回答6:
If you don't need any of the extra features provided by List<T>
, HashSet<T>
etc then returning an IEnumerable<T>
is fine, imo.
Return whatever type you feel is most appropriate; if you only need to loop over the collections or do LINQ-y stuff with them then IEnumerable<T>
is good in most situations.
When you do need to return "richer" types then consider returning an interface rather than a concrete type -- IList<T>
rather than List<T>
, ISet<T>
rather than HashSet<T>
etc -- so that your implementation can change in future, if necessary, without breaking any calling code.
回答7:
It depends. If you intend usage of your method is merely enumeration, then IEnumerable is the right choice.
If specific features like index lookup is important, then you could be more specific.
That said, you can always wrap your IEnumerable in a LINQ expression and use it as whatever. A clever compiler could optimize that expression if your returned type already supports the feature used in your LINQ expression.
回答8:
IEnumerable is faster for querying as it can combine the query logic. I typically use arrays for deleting or changing values as I do not want to change my collection while I am affecting it
来源:https://stackoverflow.com/questions/5156561/should-i-favour-ienumerablet-or-arrays