views:

542

answers:

14

What's the preferred container type when returning multiple objects of the same type from a function?

Is it against good practice to return a simple array (like MyType[]), or should you wrap it in some generic container (like ICollection<MyType>)?

Thanks!

A: 

Use generics. It's easier to interoperate with other collections classes and the type system is more able to help you with potential errors.

The old style of returning an array was a crutch before generics.

Jason Cohen
+3  A: 

Always return an interface type that presents the greatest amount of functionality to the caller. So in your case ICollection<YourType> ought to be used.

Something interesting to note is that the BCL developers actually got this wrong in some place of the .NET framework - see this Eric Lippert blog post for that story.

Andrew Hare
As he says, they didn't get it wrong, but they didn't have generics available as an option (they were introduced in CLR 2.0).
erikkallen
I know what you are getting at but to quote the article: "Let me give you an example of where we got that horridly wrong in a very visible way in the framework." :)
Andrew Hare
+5  A: 

Why not List<T>?

From the Eric Lippert post mentioned by others, I thought I will highlight this:

If I need a sequence I’ll use IEnumerable<T>, if I need a mapping from contiguous numbers to data I’ll use a List<T>, if I need a mapping across arbitrary data I’ll use a Dictionary<K,V>, if I need a set I’ll use a HashSet<T>. I simply don’t need arrays for anything, so I almost never use them. They don’t solve a problem I have better than the other tools at my disposal.

Gulzar
Well it does dependt on what you need to do with the returned value. IList<T> and List<T> do not have the same set of methods available (List<T> has more methods available). If you do not need any of those methods returning IList<T> void be the better solution since the general rule of thumb is to return the type infering the fewest restrictions. However there is a big difference between using /argument, local variabl) and returning. when you use something you know how it's used, when you return something you can't be sure of the use of the returned
Rune FS
+14  A: 

Eric Lippert has a good article on this. In case you can't be bothered to read the entire article, the answer is: return the interface.

erikkallen
+1 Would have posted that same link if you hadn't done it already.
John Price
Great find! I'm reading it now. Thanks!
Pwninstein
A: 

What ever makes your code more readable, maintainable and easier for YOU. I would have used the simple array, simpler==better most of the time. Although I really have to see the context to give the right answer.

Itay Moav
+4  A: 
return ICollection<type>

The advantage to generic return types, is that you can change the underlying implementation without changing the code that uses it. The advantage to returning the specific type, is you can use more type specific methods.

sfossen
+1  A: 

Why not IList<MyType>?

It supports direct indexing which is hallmark for an array without removing the possibility to return a List<MyType> some day. If you want to suppress this feature, you probably want to return IEnumerable<MyType>.

Mehrdad Afshari
+4  A: 

If the collection that is being returned is read-only, meaning you never want the elements to in the collection to be changed, then use IEnumerable<T>. This is the most basic representation of a read-only sequence of immutable (at least from the perspective of the enumeration itself) elements.

If you want it to be a self-contained collection that can be changed, then use ICollection<T> or IList<T>.

For example, if you wanted to return the results of searching for a particular set of files, then return IEnumerable<FileInfo>.

However, if you wanted to expose the files in a directory, however, you would expose IList/ICollection<FileInfo> as it makes sense that you would want to possibly change the contents of the collection.

casperOne
IEnumerabl<T> is _not_ a read-only collection. It's a general interface for a sequence and wether the elements are immutable or not depends on objects of T being immutable or not. any object of List<T> is an IEnumerable<T> but that certainly does not make List<T> a read-only collection
Rune FS
@runefs: You misread. I'm not speaking about the immutability of the instances returned, but rather, the immutability of the sequence itself.
casperOne
A: 

There are big advantages to favouring IEnumerable over anything else, as this gives you the greatest implementation flexibility and allows you to use yield return or Linq operators for lazy implementation.

If the caller wants a List<T> instead they can simply call ToList() on whatever you returned, and the overall performance will be roughly the same as if you had created and returned a new List<T> from your method.

Daniel Earwicker
A: 

Array is harmful, but ICollection<T> is also harmful.

ICollection<T> cannot guarantee the object will be immutable.

My recommendation is to wrap the returning object with ReadOnlyCollection<T>

chaowman
+11  A: 

Return an IEnumerable<T> using a yield return.

Daniel A. White
Interesting suggestion, I never even thought of that one.
Matthew Scharley
I have been starting to use this a lot, and it really makes for some really elegant code with LINQ.
Daniel A. White
+1 I didn't agree until you added "using a yield return". Iterator blocks are a very powerful tool that, if used properly, can streamline your code and your memory footprint.
Andrew Hare
@Daniel: 6 upvotes for the least expansive answer? Maybe you could provide some more detail to make this a useful post?
Dan
Very useful in specific cases, but not the most correct in the general sense, thanks alot for the input though.
Matthew Scharley
@vorpal: It may be the shortest in words, but it *is* the best answer here. This allows for delayed execution and all kinds of Good Stuff™. Google the yield keyword for more information.
configurator
@monoxide: In most cases this is better. In almost all cases, if you are creating the list anyway, you can simply use this yield return and call .ToList() or .ToArray() if you actually need a list or array.
configurator
@configurator: IEnumerable<T> doesn't have a .ToArray() though, and I'm coding against .net2.0, so LINQ isn't possible either.
Matthew Scharley
@configurator: My point is that thers was no supporting info in the answer. Sure we can all google but for coding topics, this site is intended to be where google searches end, not where they begin.
Dan
+8  A: 

I would return an IList<T> as that gives the consumer of your function the greatest flexibility. That way if the consumer of your function only needed to enumerate the sequence they can do so, but if they want to use the sequence as a list they can do that as well.

My general rule of thumb is to accept the least restrictive type as a parameter and return the richest type I can. This is, of course, a balancing act as you don't want to lock yourself into any particular interface or implementation (but always, always try to use an interface).

This is the least presumptuous approach that you, the API developer, can take. It is not up to you to decide how a consumer of your function will use what they send you - that is why you would return an IList<T> in this case as to give them the greatest flexibility. Also for this same reason you would never presume to know what type of parameter a consumer will send you. If you only need to iterate a sequence sent to you as a parameter then make the parameter an IEnumerable<T> rather than a List<T>.


EDIT (monoxide): Since it doesn't look like the question is going to be closed, I just want to add a link from the other question about this: Why arrays are harmful

Andrew Hare
Well said.
Shog9
+1  A: 

It depends on what you plan to do with the collection you're returning. If you're just iterating, or if you only want the user to iterate, then I agree with @Daniel, return IEnumerable<T>. If you actually want to allow list-based operations, however, I'd return IList<T>.

Randolpho
+4  A: 

A good piece of advice that I've oft heard quoted is this:

Be liberal in what you accept, precise in what you provide.

In terms of designing your API, I'd suggest you should be returning an Interface, not a concrete type.

Taking your example method, I'd rewrite it as follows:

public IList<object> Foo() 
{
    List<object> retList = new List<object>();
    // Blah, blah, [snip]
    return retList;
}

The key is that your internal implementation choice - to use a List - isn't revealed to the caller, but you're returning an appropriate interface.

Microsoft's own guidelines on framework development recommend against returning specific types, favoring interfaces. (Sorry, I couldn't find a link for this)

Similarly, your parameters should be as general as possible - instead of accepting an array, accept an IEnumerable of the appropriate type. This is compatible with arrays as well as lists and other useful types.

Taking your example method again:

public IList<object> Foo(IEnumerable<object> bar) 
{
    List<object> retList = new List<object>();
    // Blah, blah, [snip]
    return retList;
}
Bevan