Relation between IEnumerable and arrays - c#

What makes .Net IEnumerable so special that compiler types like arrays can be passed as an argument in its place with it being a class library interface. Is there some sort of inheritance in the background?
What actually happens when an array can interchangeably replace a collection or IEnumerable as a parameter:
public void DoJob(IEnumerable<Job> jobs)
{
}
Like this method call:
Job job = new Job();
DoJob(new [] { job });

Since you came from Java, I will start by telling you that in C#, there is a unified type system, where every type derives from object, unlike in Java where there are special "primitives".
So in C#, arrays are just another type. Who says they can't implement interfaces? They can! All the compiler provides is some syntax for creating them. In actuality, arrays' type is System.Array.
This is its declaration:
public abstract class Array : ICloneable, IList, ICollection,
IEnumerable, IStructuralComparable, IStructuralEquatable
See IEnumerable in there?
EDIT:
For IEnumerable<T>, we can find it in the language spec:
Section 12.1.2:
A one-dimensional array T[] implements the interface
System.Collections.Generic.IList (IList for short) and its base
interfaces.
IList<T> implements IEnumerable<T>.

Arrays are a special case in .NET, because you are not allowed to directly inherit from the Array class.
From the documentation:
Single-dimensional arrays implement the IList<T>, ICollection<T>, IEnumerable<T>, IReadOnlyList<T> and IReadOnlyCollection<T> generic interfaces. The implementations are provided to arrays at run time, and as a result, the generic interfaces do not appear in the declaration syntax for the Array class.
This is due to the fact that generics did not exist in .NET 1.0, and to prevent breaking changes, a compiler hack had to be applied.

IEnumerable is an Interface and what you're referring to is Polymorphism.
Arrays, Lists, Collections, and many other types in .NET have a base IEnumerable interface at a minimum. If you make the parameter request Job[] then only an array of Job could be used, meaning a List or other collection would have to be converted to an Array before it could be passed to the parameter. This means the method, hopefully is dealing with and expecting the array for a specific reason. If the method doesn't care about the type of collection, list, or array and simply wants to iterate through the items, then it should look lower in the inheritance tree so that many types can use the method. Making the method IEnumerable<Job> allows the method to work with ANY type that derives from IEnumerable<T> where, in this case, T is of type Job.
Knowing this a List<Job>, ObservableCollection<Job>, Job[] or linq query with select of Job plus many more can use this method.

Related

What's the minimum type requirement for a materialized collection?

If I'm serializing a collection of objects, what's the minimum type to guarantee that the collection is materialized (and not the result of an expression)?
e.g.. this should not be possible:
var message = new MyMessage {
Recipients = someCollection.Select... // <- this can't be deserialized without calling ToList()
}
I know IList would do the job, but want to know if this is the minimum requirement.
I know IList would do the job
Actually, it would not. No interface can ever possibly have such a guarantee, because I can always make an implementation of that interface that defers materialization of any query until some of the interface methods are called. Now, by convention, a type implementing IList would be a materialized collection, and not something deferring work, but that is not a guarantee.
To have a guarantee you'll need to use a concrete type rather than an interface, accepting an array, List, etc.
Of course one option that you have is to accept an interface such as IEnumerable and then materialize it into a collection yourself (possibly as a 3rd overload with overloads for arrays and lists to avoid the need to re-materialize those) because if you've materialized it yourself then you know it's not deferring execution.
You can't. Whether a collection is materialized or a query is ultimately an implementation detail that is not exposed via any normal public API. However, if you want to be reasonably sure given common types, you can use ICollection<T>.
ICollection<T> requires that the Count be available. IList<T> further requires that you can access elements by index. Since you don't appear to need anything beyond IEnumerable<T>, I'd recommend ICollection<T>, e.g.
public ICollection<Recipient> Recipients { get; set; }
It may also be useful for you to create an IMaterialized or IMaterializedEnumerable<T> : IEnumerable<T> marker interface and class(es) that implement it, if ensuring that a collection is materialized is more important than being able to easily use built-in types and methods.

How do arrays in C# partially implement IList<T>?

So as you may know, arrays in C# implement IList<T>, among other interfaces. Somehow though, they do this without publicly implementing the Count property of IList<T>! Arrays have only a Length property.
Is this a blatant example of C#/.NET breaking its own rules about the interface implementation or am I missing something?
So as you may know, arrays in C# implement IList<T>, among other interfaces
Well, yes, erm no, not really. This is the declaration for the Array class in the .NET 4 framework:
[Serializable, ComVisible(true)]
public abstract class Array : ICloneable, IList, ICollection, IEnumerable,
IStructuralComparable, IStructuralEquatable
{
// etc..
}
It implements System.Collections.IList, not System.Collections.Generic.IList<>. It can't, Array is not generic. Same goes for the generic IEnumerable<> and ICollection<> interfaces.
But the CLR creates concrete array types on the fly, so it could technically create one that implements these interfaces. This is however not the case. Try this code for example:
using System;
using System.Collections.Generic;
class Program {
static void Main(string[] args) {
var goodmap = typeof(Derived).GetInterfaceMap(typeof(IEnumerable<int>));
var badmap = typeof(int[]).GetInterfaceMap(typeof(IEnumerable<int>)); // Kaboom
}
}
abstract class Base { }
class Derived : Base, IEnumerable<int> {
public IEnumerator<int> GetEnumerator() { return null; }
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); }
}
The GetInterfaceMap() call fails for a concrete array type with "Interface not found". Yet a cast to IEnumerable<> works without a problem.
This is quacks-like-a-duck typing. It is the same kind of typing that creates the illusion that every value type derives from ValueType which derives from Object. Both the compiler and the CLR have special knowledge of array types, just as they do of value types. The compiler sees your attempt at casting to IList<> and says "okay, I know how to do that!". And emits the castclass IL instruction. The CLR has no trouble with it, it knows how to provide an implementation of IList<> that works on the underlying array object. It has built-in knowledge of the otherwise hidden System.SZArrayHelper class, a wrapper that actually implements these interfaces.
Which it doesn't do explicitly like everybody claims, the Count property you asked about looks like this:
internal int get_Count<T>() {
//! Warning: "this" is an array, not an SZArrayHelper. See comments above
//! or you may introduce a security hole!
T[] _this = JitHelpers.UnsafeCast<T[]>(this);
return _this.Length;
}
Yes, you can certainly call that comment "breaking the rules" :) It is otherwise darned handy. And extremely well hidden, you can check this out in SSCLI20, the shared source distribution for the CLR. Search for "IList" to see where the type substitution takes place. The best place to see it in action is clr/src/vm/array.cpp, GetActualImplementationForArrayGenericIListMethod() method.
This kind of substitution in the CLR is pretty mild compared to what happens in the language projection in the CLR that allows writing managed code for WinRT (aka Metro). Just about any core .NET type gets substituted there. IList<> maps to IVector<> for example, an entirely unmanaged type. Itself a substitution, COM doesn't support generic types.
Well, that was a look at what happens behind the curtain. It can be very uncomfortable, strange and unfamiliar seas with dragons living at the end of the map. It can be very useful to make the Earth flat and model a different image of what's really going on in managed code. Mapping it to everybody favorite answer is comfortable that way. Which doesn't work so well for value types (don't mutate a struct!) but this one is very well hidden. The GetInterfaceMap() method failure is the only leak in the abstraction that I can think of.
New answer in the light of Hans's answer
Thanks to the answer given by Hans, we can see the implementation is somewhat more complicated than we might think. Both the compiler and the CLR try very hard to give the impression that an array type implements IList<T> - but array variance makes this trickier. Contrary to the answer from Hans, the array types (single-dimensional, zero-based anyway) do implement the generic collections directly, because the type of any specific array isn't System.Array - that's just the base type of the array. If you ask an array type what interfaces it supports, it includes the generic types:
foreach (var type in typeof(int[]).GetInterfaces())
{
Console.WriteLine(type);
}
Output:
System.ICloneable
System.Collections.IList
System.Collections.ICollection
System.Collections.IEnumerable
System.Collections.IStructuralComparable
System.Collections.IStructuralEquatable
System.Collections.Generic.IList`1[System.Int32]
System.Collections.Generic.ICollection`1[System.Int32]
System.Collections.Generic.IEnumerable`1[System.Int32]
For single-dimensional, zero-based arrays, as far as the language is concerned, the array really does implement IList<T> too. Section 12.1.2 of the C# specification says so. So whatever the underlying implementation does, the language has to behave as if the type of T[] implements IList<T> as with any other interface. From this perspective, the interface is implemented with some of the members being explicitly implemented (such as Count). That's the best explanation at the language level for what's going on.
Note that this only holds for single-dimensional arrays (and zero-based arrays, not that C# as a language says anything about non-zero-based arrays). T[,] doesn't implement IList<T>.
From a CLR perspective, something funkier is going on. You can't get the interface mapping for the generic interface types. For example:
typeof(int[]).GetInterfaceMap(typeof(ICollection<int>))
Gives an exception of:
Unhandled Exception: System.ArgumentException: Interface maps for generic
interfaces on arrays cannot be retrived.
So why the weirdness? Well, I believe it's really due to array covariance, which is a wart in the type system, IMO. Even though IList<T> is not covariant (and can't be safely), array covariance allows this to work:
string[] strings = { "a", "b", "c" };
IList<object> objects = strings;
... which makes it look like typeof(string[]) implements IList<object>, when it doesn't really.
The CLI spec (ECMA-335) partition 1, section 8.7.1, has this:
A signature type T is compatible-with a signature type U if and only if at least one of the following holds
...
T is a zero-based rank-1 array V[], and U is IList<W>, and V is array-element-compatible-with W.
(It doesn't actually mention ICollection<W> or IEnumerable<W> which I believe is a bug in the spec.)
For non-variance, the CLI spec goes along with the language spec directly. From section 8.9.1 of partition 1:
Additionally, a created vector with element type T, implements the interface System.Collections.Generic.IList<U>, where U := T. (§8.7)
(A vector is a single-dimensional array with a zero base.)
Now in terms of the implementation details, clearly the CLR is doing some funky mapping to keep the assignment compatibility here: when a string[] is asked for the implementation of ICollection<object>.Count, it can't handle that in quite the normal way. Does this count as explicit interface implementation? I think it's reasonable to treat it that way, as unless you ask for the interface mapping directly, it always behaves that way from a language perspective.
What about ICollection.Count?
So far I've talked about the generic interfaces, but then there's the non-generic ICollection with its Count property. This time we can get the interface mapping, and in fact the interface is implemented directly by System.Array. The documentation for the ICollection.Count property implementation in Array states that it's implemented with explicit interface implementation.
If anyone can think of a way in which this kind of explicit interface implementation is different from "normal" explicit interface implementation, I'd be happy to look into it further.
Old answer around explicit interface implementation
Despite the above, which is more complicated because of the knowledge of arrays, you can still do something with the same visible effects through explicit interface implementation.
Here's a simple standalone example:
public interface IFoo
{
void M1();
void M2();
}
public class Foo : IFoo
{
// Explicit interface implementation
void IFoo.M1() {}
// Implicit interface implementation
public void M2() {}
}
class Test
{
static void Main()
{
Foo foo = new Foo();
foo.M1(); // Compile-time failure
foo.M2(); // Fine
IFoo ifoo = foo;
ifoo.M1(); // Fine
ifoo.M2(); // Fine
}
}
IList<T>.Count is implemented explicitly:
int[] intArray = new int[10];
IList<int> intArrayAsList = (IList<int>)intArray;
Debug.Assert(intArrayAsList.Count == 10);
This is done so that when you have a simple array variable, you don't have both Count and Length directly available.
In general, explicit interface implementation is used when you want to ensure that a type can be used in a particular way, without forcing all consumers of the type to think about it that way.
Edit: Whoops, bad recall there. ICollection.Count is implemented explicitly. The generic IList<T> is handled as Hans descibes below.
Explicit interface implementation. In short, you declare it like void IControl.Paint() { } or int IList<T>.Count { get { return 0; } }.
It's no different than an explicit interface implementation of IList. Just because you implement the interface doesn't mean its members need to appear as class members. It does implement the Count property, it just doesn't expose it on X[].
With reference-sources being available:
//----------------------------------------------------------------------------------------
// ! READ THIS BEFORE YOU WORK ON THIS CLASS.
//
// The methods on this class must be written VERY carefully to avoid introducing security holes.
// That's because they are invoked with special "this"! The "this" object
// for all of these methods are not SZArrayHelper objects. Rather, they are of type U[]
// where U[] is castable to T[]. No actual SZArrayHelper object is ever instantiated. Thus, you will
// see a lot of expressions that cast "this" "T[]".
//
// This class is needed to allow an SZ array of type T[] to expose IList<T>,
// IList<T.BaseType>, etc., etc. all the way up to IList<Object>. When the following call is
// made:
//
// ((IList<T>) (new U[n])).SomeIListMethod()
//
// the interface stub dispatcher treats this as a special case, loads up SZArrayHelper,
// finds the corresponding generic method (matched simply by method name), instantiates
// it for type <T> and executes it.
//
// The "T" will reflect the interface used to invoke the method. The actual runtime "this" will be
// array that is castable to "T[]" (i.e. for primitivs and valuetypes, it will be exactly
// "T[]" - for orefs, it may be a "U[]" where U derives from T.)
//----------------------------------------------------------------------------------------
sealed class SZArrayHelper {
// It is never legal to instantiate this class.
private SZArrayHelper() {
Contract.Assert(false, "Hey! How'd I get here?");
}
/* ... snip ... */
}
Specifically this part:
the interface stub dispatcher treats this as a special case, loads up
SZArrayHelper, finds the corresponding generic method (matched simply
by method name), instantiates it for type and executes it.
(Emphasis mine)
Source (scroll up).

Does an ICollection<T> have an order?

Following the rules that a public APIs should never return a list, i'm blinding converting all code that returned lists, to return ICollection<T> instead:
public IList<T> CommaSeparate(String value) {...}
becomes
public ICollection<T> CommaSeparate(String value) {...}
And although an ICollection has a Count, there is no way to get items by that index.
And although an ICollection exposes an enumerator (allowing foreach), i see no guarantee that the order of enumeration starts at the "top" of the list, as opposed to the "bottom".
i could mitigate this by avoiding the use of ICollection, and instead use Collection:
public Collection<T> Commaseparate(String value) {...}
This allows the use of an Items[index] syntax.
Unfortunately, my internal implementation constructs an array; which i can be cast to return IList or ICollection, but not as a Collection.
Is there a ways to access items of a collection in order?
This begs the wider question: Does an ICollection even have an order?
Conceptually, imagine i want to parse a command line string. It is critical that the order of items be maintained.
Conceptually, i require a contract that indicates an "ordered" set of string tuples. In the case of an API contract, to indicate order, which of the following is correct:
IEnumerable<String> Grob(string s)
ICollection<String> Grob(string s)
IList<String> Grob(string s)
Collection<String> Grob(string s)
List<String> Grob(string s)
The ICollection<T> interface doesn't specify anything about an order. The objects will be in the order specified by the object returned. For example, if you return the Values collection of a SortedDictionary, the objects will be in the the order defined by the dictionary's comparer.
If you need the method to return, by contract, a type whose contract requires a certain order, then you should express that in the method's signature by returning the more specific type.
Regardless of the runtime type of the object returned, consider the behavior when the static reference is IList<T> or ICollection<T>: When you call GetEnumerator() (perhaps implicitly in a foreach loop), you're going to call the same method and get the same object regardless of the static type of the reference. It will therefore behave the same way regardless of the CommaSeparate() method's return type.
Additional thought:
As someone else pointed out, the FXCop rule warns against using List<T>, not IList<T>; the question you linked to is asking why FXCop doesn't recommend using IList<T> in place of List<T>, which is another matter. If I imagine that you are parsing a command-line string where order is important, I would stick with IList<T> if I were you.
ICollection does not have a guaranteed order, but the class that actually implements it may (or may not).
If you want to return an ordered collection, then return an IList<T> and don't get too hung up on FxCop's generally sound, but very generic, advice.
No, ICollection does not imply an order.
The ICollection instance has the "order" of whatever class that implements it. That is, referencing a List<T> as an ICollection will not alter its order at all.
Likewise, if you access an unordered collection as an ICollection, it will not impose an order on the unordered collection either.
So, to your question:
Does an ICollection even have an order?
The answer is: it depends solely on the class that implements it.
ICollection<T> may have an order, but the actual ordering depends on the class implementing it.
It does not have accesor for an item at given index. IList<T> specializes this interface to provide access by index.
An ICollection<T> is just an interface; whether it's ordered or not is entirely dependent up the implementation underlying it (which is supposed to be opaque).
If you want to be able to access it by index, you'd want to return things as an IList<T>, which is both IEnumerable<T> and ICollection<T>. One should bear in mind, though, that depending on the underlying implementation, that getting at an arbitrary item in the collection could require O(N/2) time on the average.
My inclination would be to avoid the 'collection' interfaces altogether and instead use a custom type representing the collection in terms of the problem domain and exposing the appropriate logical operations suitable for that type.
ICollection is just an interface—there is no implementation or explicit specification about ordering. That means if you return something that enumerates in an ordered manner, whatever is consuming your ICollection will do so in an ordered manner.
Order is only implied by the underlying, implementing, object. There is no specification in ICollection that says it should be ordered or not. Enumerating over a result multiple times will invoke the underlying object's enumerator, which is the only place that those rules would be set. An object doesn't change the way it is enumerated just because it inherits this interface. Had the interface specified that it is an ordered result, then you could safely rely on the ordered result of the implementing object.
It depends on the implementation of the instance. An ICollection that happens to be a List has an order, an ICollection that happens to be a Collection does not.
All ICollections implement IEnumerable, which returns the items one at a time, ordered or otherwise.
EDIT: In reply to your additional example about command line parsing in the question, I would argue that the appropriate return type depends on what you are doing with those arguments afterward, but IEnumerable is probably the right one.
My reasoning is that IList, ICollection, and their concrete implementations permit modification of the list returned from Grob, which you probably don't want. Since .NET doesn't have an Indexed Sequence interface, IEnumerable is the best bet to prevent your callers from doing something weird like trying to modify the parameter list that they get back.
If you expect that all present and future versions of your method will have no difficulty returning an object that will be able to quickly and easily retrieve the Nth item, use type IList<T> to return a reference to something that implements both IList<T> and non-generic ICollection. If you expect that some present or future versions might not be able to quickly and easily return the Nth item, but would be able to instantly report the number of items, use type ICollection<T> to return a reference something that implements ICollection<T> and non-generic ICollection. If you expect that present or future versions may have trouble even knowing how many items there are, return IEnumerable<T>. The question of sequencing is irrelevant; the ability to access the Nth thing implies that a defined sequence exists, but ICollection<T> says neither more nor less about sequencing than IEnumerable<T>.

Why array implements IList?

See the definition of System.Array class
public abstract class Array : IList, ...
Theoretically, I should be able to write this bit and be happy
int[] list = new int[] {};
IList iList = (IList)list;
I also should be able to call any method from the iList
ilist.Add(1); //exception here
My question is not why I get an exception, but rather why Array implements IList?
Because an array allows fast access by index, and IList/IList<T> are the only collection interfaces that support this. So perhaps your real question is "Why is there no interface for constant collections with indexers?" And to that I have no answer.
There are no readonly interfaces for collections either. And I'm missing those even more than a constant sized with indexers interface.
IMO there should be several more (generic) collection interfaces depending on the features of a collection. And the names should have been different too, List for something with an indexer is really stupid IMO.
Just Enumeration IEnumerable<T>
Readonly but no indexer (.Count, .Contains,...)
Resizable but no indexer, i.e. set like (Add, Remove,...) current ICollection<T>
Readonly with indexer (indexer, indexof,...)
Constant size with indexer (indexer with a setter)
Variable size with indexer (Insert,...) current IList<T>
I think the current collection interfaces are bad design. But since they have properties telling you which methods are valid (and this is part of the contract of these methods), it doesn't break the substitution principle.
The remarks section of the documentation for IList says:
IList is a descendant of the
ICollection interface and is the base
interface of all non-generic lists.
IList implementations fall into three
categories: read-only, fixed-size, and
variable-size. A read-only IList
cannot be modified. A fixed-size IList
does not allow the addition or removal
of elements, but it allows the
modification of existing elements. A
variable-size IList allows the
addition, removal, and modification of
elements.
Obviously, arrays fall into the fixed-size category, so by the definition of the interface it makes sense.
Because not all ILists are mutable (see IList.IsFixedSize and IList.IsReadOnly), and arrays certainly behave like fixed-size lists.
If your question is really "why does it implement a non-generic interface", then the answer is that these were around before generics came along.
It's a legacy that we have from the times when it wasn't clear how to deal with read only collections and whether or not Array is read only. There are IsFixedSize and IsReadOnly flags in the IList interface. IsReadOnly flag means that collection can't be changed at all and IsFixedSize means that collection does allow modification, but not adding or removal of items.
At the time of .Net 4.5 it was clear that some "intermediate" interfaces are required to work with read only collections, so IReadOnlyCollection<T> and IReadOnlyList<T> were introduced.
Here is a great blog post describing the details: Read only collections in .NET
Definition of IList interface is "Represents a non-generic collection of objects that can be individually accessed by index.". Array completely satisfies this definition, so must implement the interface.
Exception when calling Add() method is "System.NotSupportedException: Collection was of a fixed size" and occurred because array can not increase its capacity dynamically. Its capacity is defined during creation of array object.
Having an array implement IList (and transitively, ICollection) simplified the Linq2Objects engine, since casting the IEnumerable to IList/ICollection would also work for arrays.
For example, a Count() ends up calling the Array.Length under-the-hood, since it's casted to ICollection and the array's implementation returns Length.
Without this, the Linq2Objects engine would not have special treatment for arrays and perform horribly, or they'd need to double the code adding special-case treatment for arrays (like they do for IList). They must've opted to make array implement IList instead.
That's my take on "Why".
Also implementation details LINQ Last checks for IList , if it did not implement list they would need either 2 checks slowing down all Last calls or have Last on an Array taking O(N)
An Array is just one of many possible implementations of IList.
As code should be loosely coupled, depend on abstractions and what not... The concrete implementation of IList that uses consecutive memory (an array) to store it's values is called Array. We do not "add" IList to the Array class that's just the wrong order of reasoning; Array implements IList as an array.
The exception is exactly what the interface defines. It is not a surprise if you know the whole interface not just a single method. The interface also give you the opportunity to check the IsFixedSize property and see if it is safe to call the Add method.

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:
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? 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.
The reason this is preferred to a simple IList is that IList does not inform the caller if its read only or not.
If you return an IEnumerable<T> instead, certain operations may be a little trickier for the caller to perform. 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. So, for example, if you perform a Count and the underlying collection is a List it will NOT walk through all the elements.
Personally, for an ORM I would probably stick with Collection<T> as my return value.
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>:
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>.
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.
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. If your method could return either an IList or an IEnumerable, prefer returning 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.
Returning a more derived type (IList) provides the users of your API with more operations on the result.
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.
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. When practical, I like to avoid converting the results of LINQ-to-Objects to a concrete List or Dictionary until I have to. 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<T> offers the calling code many more features, such as modifying the returned object and access by index. 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).
I think you can use either, but each has a use. Basically List is IEnumerable but you have
count functionality, add element, remove element
IEnumerable is not efficient for counting elements
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.
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.
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, e.g. manufacturer.Models.Add(model) violates law of demeter. 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.)
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.
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. But, if you return an IList, he will know that he can iterate over it. 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.
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.
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.
If a method returns a read-only collection, show that by using IReadOnlyList or IReadOnlyCollection as the return value type.
More
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.
Returning IList my votes are always againist it but it's mainly what you like and what not.
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).
However if you need items count, don't forget that there is another layer between IEnumerable and 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>?
From what I remember, Collection<T> was the preferred return type over List<T> because it abstracts away the implementation. They all implement IEnumerable, but that sounds to me a bit too low-level for the job.
I think you can use either, but each has a use. Basically List is IEnumerable but you have count functionality, Add element, remove element
IEnumerable is not efficient for counting elements, or getting a specific element in the collection.
List is a collection which is ideally suited to finding specific elements, easy to add elements, or remove them.
Generally I try to use List where possible as this gives me more flexibility.
Use
List<FooBar> getRecentItems()
rather than
IList<FooBar> GetRecentItems()
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.
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! 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.
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#.
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. List<T> is in fact a Supreme Pizza that always contains all the Interface ingredients combined (ICollection, IEnumerable, IList, etc).
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:
// 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. You can only access IEnumerable<T> members and cannot see all the other Interface members in 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)!
Here is the List<T> class, showing you WHY that is. Notice the List<T> in the .NET Library has implemented all the other Interfaces including IList!! But IEnumerable<T> implements just a small subsection of those List Interface members.
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?
Instantiating a List<T> as List<T> gives you access to all Interface members! 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. Who needs Supreme Pizza every time?
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>. If your variable is an Interface type, you can now switch out the object created with something other than List<T>. 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.

Categories