You only use IEnumerable<T> if you want a smaller set of features. Use List<T> if you plan to use a larger, richer set of features.
One important difference between IEnumerable and List (besides one being an interface and the other being a concrete class) is that IEnumerable is read-only and List is not. So if you need the ability to make permanent changes of any kind to your collection (add & remove), you'll need List.
IEnumerable has just one method called GetEnumerator. This method returns another type which is an interface that interface is IEnumerator. If we want to implement enumerator logic in any collection class, it needs to implement IEnumerable interface (either generic or non-generic).
Yes, ToList will create a new list, but because in this case MyObject is a reference type then the new list will contain references to the same objects as the original list.
Returning List<T>
has the advantage that those methods of List<T>
that are not part of IList<T>
are easily used. There are a lot of things you can do with a List<T>
that you cannot do with a IList<T>
.
In contrast, Lookup<TKey, TElement>
has only one available method that ILookup<TKey, TElement>
does not have (ApplyResultSelector
), and you probably would not end up using that anyway.
These kind of decisions may feel arbitrary but I guess that ToList()
returns List<T>
rather than an interface because List<T>
both implements IList<T>
but it adds other members not present in a regular IList<T>
-typed object.
For example, AddRange()
.
See what IList<T>
should implement (http://msdn.microsoft.com/en-us/library/5y536ey6.aspx):
public interface IList<T> : ICollection<T>,
IEnumerable<T>, IEnumerable
And List<T>
(http://msdn.microsoft.com/en-us/library/6sh2ey19.aspx):
public class List<T> : IList<T>, ICollection<T>,
IList, ICollection, IReadOnlyList<T>, IReadOnlyCollection<T>, IEnumerable<T>,
IEnumerable
Maybe your own code doesn't require IReadOnlyList<T>
, IReadOnlyCollection<T>
or ICollection
, but other components on .NET Framework and other products may rely on a more specialized list object and that's why .NET dev team decided to do not return an interface.
Don't feel always return an interface is the best practice. It's if your code or third-party ones require such encapsulation.
There are a number of advantages to just having a List
over an IList
. To begin with, List
has methods that IList
does not. You also know what the implementation is which allows you to reason about how it will behave. You know it can efficiently add to the end, but not the start, you know that it's indexer is very fast, etc.
You don't need to worry about your structure being changed to a LinkedList
and wrecking the performance of your application. When it comes to data structures like this it really is important in quite a lot of contexts to know how your data structure is implemented, not just the contract that it follows. It's behavior that shouldn't ever change.
You also can't pass an IList
to a method accepting a List
, which is something that you see quite a lot of. ToList
is frequently used because the person really needs an instance of List
, to match a signature they can't control, and IList
doesn't help with that.
Then we ask ourselves what advantages there are to returning IList
. Well, we could possibly return some other implementation of a list, but as mentioned before this is likely to have very detrimental consequences, almost certainly much more than could possibly be gained from using any other type. It might give you warm fuzzies to be using an interface instead of an implementation, but even that is something I don't feel is a good mentality (in general or) in this context. As a rule returning an interface is generally not preferable to returning a concrete implementation. "Be liberal in what you accept and specific in what you provide." The parameters to your methods should, where possible, be interfaces defining the least amount of functionality you need to that your caller can pass in any implementation that does what you need of it, and you should provide as concrete of an implementation as the caller is "allowed" to see so that they can do as much with the result as that object is capable of. Passing an interface is restricting that value, which is only occasionally something that you want to do.
So now we move onto, "Why return ILookup
and not Lookup
?" Well, first off Lookup
isn't a public class. There is no Lookup
in System.Collections.*
. The Lookup
class that is exposed through LINQ exposes no constructors publicly. You're not able to use the class except through ToLookup
. It also exposes no functionality that isn't already exposed through ILookup
. In this particular case they designed the interface specifically around this exact method (ToLookup
) and the Lookup
class is a class specifically designed to implement that interface. Because of all of this virtually all of the points discussed about List
just don't apply here. Would it have been a problem to return Lookup
instead, no, not really. In this case it really just doesn't matter much at all either way.
In my opinion returning a List<T>
is justified by the fact that the method name says ToList
. Otherwise it would have to be named ToIList
. It is the very purpose of this method to convert an unspecific IEnumerable<T>
to the specific type List<T>
.
If you had a method with an unspecific name like GetResults
, then a return type like IList<T>
or IEnumerable<T>
would seem appropriate to me.
If you look at the implementation of the Lookup<TKey, TElement>
class with reflector, you'll see a lot of internal
members, that are only accessible to LINQ itself. There is no public constructor and Lookup
objects are immutable. Therefore there would be no advantage in exposing Lookup
directly.
Lookup<TKey, TElement>
class seems to be kind of LINQ-internal and is not meant for public use.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With