Related
So in my class I have this private readonly member ICollection<IMusicItem> playlist. I would prefer to use the interface ICollection<T>.
I would like to use the List<T>.AddRange(IEnumerable<T> items). In my method would it be dangerous to cast the ICollection to a List<T> even if I instantiate the ICollection<T> as a new List<T>() in the constructor.
Is this bad practice, is there a better way of doing this?
Or is it just better to have a List<T>
It's bad practice, because it breaks encapsulation. Using an interface is good, but it's pointless if you have to cast the object back to a concrete type. Act as if you didn't know the concrete type, or it's a future bug waiting to happen if you decide to switch to another type later on.
Use an extension method instead:
public static void AddRange<T>(this ICollection<T> collection, IEnumerable<T> items)
{
foreach (var item in items)
collection.Add(item);
}
Note: It's better to expose interfaces in a public API so you're free to change the implementing object later, but it's a matter of style whether to do it on private fields. You may as well use the concrete class.
It's not dangerous (when done right), just pointless.
if (playlist is IList<IMusicItem>)
{
(playList as IList<IMusicItem>).AddRange(items);
}
else
{
// still need a foreach here
}
The real issue is "I would prefer to use the interface ICollection<T>".
Why exactly? Your question suggests it always is a List, so why not expose it as such?
Exposing it as a more general ICollection<> only makes sense when other implementations than List might exist, and then the casting is useless.
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).
The extension method ToList() returns a List<TSource>. Following the same pattern, ToDictionary() returns a Dictionary<TKey, TSource>.
I am curious why those methods do not type their return values as IList<TSource> and IDictionary<TKey, TSource> respectively. This seems even odder because ToLookup<TSource, TKey> types its return value as an interface instead of an actual implementation.
Looking at the source of those extension methods using dotPeek or other decompiler, we see the following implementation (showing ToList() because it is shorter):
public static List<TSource> ToList<TSource>(this IEnumerable<TSource> source) {
if (source == null) throw Error.ArgumentNull("source");
return new List<TSource>(source);
}
So why does this method type its return value as a specific implementation of the interface and not the interface itself? The only change would be the return type.
I am curious because the IEnumerable<> extensions are very consistent in their signatures, except for those two cases. I always thought it to be a bit strange.
Additionally, to make things even more confusing, the documentation for ToLookup() states:
Creates a Lookup from an IEnumerable according to a
specified key selector function.
but the return type is ILookup<TKey, TElement>.
In Edulinq, Jon Skeet mentions that the return type is List<T> instead of IList<T>, but does not touch the subject further.
Extensive searching has yielded no answer, so here I ask you:
Is there any design decision behind not typing the return values as interfaces, or is it just happenstance?
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.
I believe that the decision to return a List<> instead of an IList<> is that one of the more common use cases for calling ToList is to force immediate evaluation of the entire list. By returning a List<> this is guaranteed. With an IList<> the implementation can still be lazy, which would defeat the "primary" purpose of the call.
This is one of the common things that programmers have difficulty understanding around the use of interfaces and concrete types.
Returning a concrete List<T> that implements IList<T> only gives the method consumer more information. Here is what the List object implements (via MSDN):
[SerializableAttribute]
public class List<T> : IList<T>, ICollection<T>, IList, ICollection,
IReadOnlyList<T>, IReadOnlyCollection<T>, IEnumerable<T>, IEnumerable
Returning as a List<T> gives us the ability to call members on all of these interfaces in addition to List<T> itself. For example we could only use List.BinarySearch(T) on a List<T>, as it exists in List<T> but not in IList<T>.
In general to maximize flexibility of our methods, we should take the most abstract types as parameters (ie. only the things we're going to use) and return the least abstract type possible (to allow a more functional return object).
In general when you call ToList() on a method you're looking for a concrete type otherwise the item could stay as type IEnumerable. You don't need to convert to a List unless you're doing something that requires a concrete list.
The short answer is that in general returning the most specific type available is recommended by the authoritative Framework Design Guidelines. (sorry I don't have a citation on hand, but I remember this clearly since it stuck out in contrast to the Java community guidelines which prefer the opposite).
This makes sense to me. You can always do e.g. IList<int> list = x.ToList(), only the library author needs to be concerned with being able to support the concrete return type.
ToLookup<T> is the unique one in the crowd. But perfectly within the guidelines: it is the most specific type available that the library authors are willing to support (as others have pointed out, the concrete Lookup<T> type appears to be more of an internal type not meant for public use).
Because List<T> actually implements a range of interfaces, not just IList:
public class List<T> : IList<T>, ICollection<T>, IList, ICollection, IReadOnlyList<T>, IReadOnlyCollection<T>, IEnumerable<T>, IEnumerable{
}
Each of those interfaces define a range of features which the List must conform. Picking one particular one, would render bulk of the implementation unusable.
If you do want to return IList, nothing stops you from having your own simple wrapper:
public static IList<TSource> ToIList<TSource>(this IEnumerable<TSource> source)
{
if (source == null) throw new ArgumentNullException(source);
return source.ToList();
}
If a function returns a newly-constructed immutable object, the caller should generally not care about the precise type returned provided it is capable of holding the actual data that it contains. For example, a function that is supposed to return an IImmutableMatrix might normally return an ImmutableArrayMatrix backed by a privately-held array, but if all the cells hold zeroes it might instead return an ZeroMatrix, backed only by Width and Height fields (with a getter that simply returns zero all the time). The caller wouldn't care whether it was given an ImmutableArrayMatrix matrix or a ZeroMatrix; both types would would allow all of their cells to be read, and guarantee their values would never change, and that's what the caller would care about.
On the other hand, functions that return newly-constructed objects that allow open-ended mutation should generally return the precise type the caller is going to expect. Unless there will be a means by which the caller can request different return types (e.g. by calling ToyotaFactory.Build("Corolla") versus ToyotaFactory.Build("Prius")) there's no reason for the declared return type to be anything else. While factories that return immutable data-holding objects can select a type based on the data to be contained, factories that return freely-mutable types will have no way of knowing what data may be put into them. If different callers will have different needs (e.g. returning to the extant example, some callers' needs would be met with an array, while others' would not) they should be given a choice of factory methods.
BTW, something like IEnumerator<T>.GetEnumerator() is a bit of a special case. The returned object will almost always be mutable, but only in a very highly-constrained fashion; indeed, it is expected that the returned object regardless of its type will have exactly one piece of mutable state: its position in the enumeration sequence. Although an IEnumerator<T> is expected to be mutable, the portions of its state which would vary in derived-class implementations are not.
If I have two classes:
public class A { }
public class B : A { }
and I create a generic container and a function that takes it:
public void Foo(List<A> lst) { ... }
I get an invalid conversion if I attempt casting a List<B> to a List<A>, and instead have to pass it like so:
var derivedList = new List<B>();
Foo(new List<A>(derivedList));
Is there some way to pass a List<B> to this function without the overhead of allocating a brand new list, or does C# not support converting from a generic container of a derived type to its base type?
A List<B> simply isn't a List<A> - after all, you can add a plain A to a List<A>, but not to a List<B>.
If you're using C# 4 and .NET 4 and your Foo method only really needs to iterate over the list, then change the method to:
public void Foo(IEnumerable<A> lst) { ... }
In .NET 4, IEnumerable<T> is covariant in T, which allows a conversion from IEnumerable<B> (including a List<B>) to IEnumerable<A>. This is safe because values only ever flow "out" of IEnumerable<A>.
For a much more detailed look at this, you can watch the video of the session I gave at NDC 2010 as part of the torrent of NDC 2010 videos.
This is not possible. C# doesn't support co / contra variance on concrete types such as List<T>. It does support it on interfaces though so if you switch Foo to the following signature you can avoid an allocation
public void Foo(IEnumerable<A> enumerable) { ...
If you wish to pass list-like things to routines which are going to read them but not write them, it would be possible to define a generic covariant IReadableList<out T> interface, so that an IReadableList<Cat> could be passed to a routine expecting an IReadableList<Animal>. Unfortunately, common existing IList<T> implementations don't implement any such thing, and so the only way to implement one would be to implement a wrapper class (which could accept an IList as a parameter), but it probably wouldn't be too hard. Such a class should also implement non-generic IList, also as read-only, to allow code to evaluate Count without having to know the type of the items in the list.
Note that an object's implementation of IReadableList<T> should not be regarded as any promise of immutability. It would be perfectly reasonable to have a read-write list or wrapper class implement IReadableList<T>, since a read-write list is readable. It's not possible to use an IReadableList<T> to modify a list without casting it to something else, but there's no guarantee a list passed as IReadableList<T> can't be modified some other way, such as by casting it to something else, or by using a reference stored elsewhere.
I know that IList is the interface and List is the concrete type but I still don't know when to use each one. What I'm doing now is if I don't need the Sort or FindAll methods I use the interface. Am I right? Is there a better way to decide when to use the interface or the concrete type?
There are two rules I follow:
Accept the most basic type that will work
Return the richest type your user will need
So when writing a function or method that takes a collection, write it not to take a List, but an IList<T>, an ICollection<T>, or IEnumerable<T>. The generic interfaces will still work even for heterogenous lists because System.Object can be a T too. Doing this will save you headache if you decide to use a Stack or some other data structure further down the road. If all you need to do in the function is foreach through it, IEnumerable<T> is really all you should be asking for.
On the other hand, when returning an object out of a function, you want to give the user the richest possible set of operations without them having to cast around. So in that case, if it's a List<T> internally, return a copy as a List<T>.
Microsoft guidelines as checked by FxCop discourage use of List<T> in public APIs - prefer IList<T>.
Incidentally, I now almost always declare one-dimensional arrays as IList<T>, which means I can consistently use the IList<T>.Count property rather than Array.Length. For example:
public interface IMyApi
{
IList<int> GetReadOnlyValues();
}
public class MyApiImplementation : IMyApi
{
public IList<int> GetReadOnlyValues()
{
List<int> myList = new List<int>();
... populate list
return myList.AsReadOnly();
}
}
public class MyMockApiImplementationForUnitTests : IMyApi
{
public IList<int> GetReadOnlyValues()
{
IList<int> testValues = new int[] { 1, 2, 3 };
return testValues;
}
}
IEnumerable
You should try and use the least specific type that suits your purpose.
IEnumerable is less specific than IList.
You use IEnumerable when you want to loop through the items in a collection.
IList
IList implements IEnumerable.
You should use IList when you need access by index to your collection, add and delete elements, etc...
List
List implements IList.
There's an important thing that people always seem to overlook:
You can pass a plain array to something which accepts an IList<T> parameter, and then you can call IList.Add() and will receive a runtime exception:
Unhandled Exception: System.NotSupportedException: Collection was of a fixed size.
For example, consider the following code:
private void test(IList<int> list)
{
list.Add(1);
}
If you call that as follows, you will get a runtime exception:
int[] array = new int[0];
test(array);
This happens because using plain arrays with IList<T> violates the Liskov substitution principle.
For this reason, if you are calling IList<T>.Add() you may want to consider requiring a List<T> instead of an IList<T>.
I would agree with Lee's advice for taking parameters, but not returning.
If you specify your methods to return an interface that means you are free to change the exact implementation later on without the consuming method ever knowing. I thought I'd never need to change from a List<T> but had to later change to use a custom list library for the extra functionality it provided. Because I'd only returned an IList<T> none of the people that used the library had to change their code.
Of course that only need apply to methods that are externally visible (i.e. public methods). I personally use interfaces even in internal code, but as you are able to change all the code yourself if you make breaking changes it's not strictly necessary.
It's always best to use the lowest base type possible. This gives the implementer of your interface, or consumer of your method, the opportunity to use whatever they like behind the scenes.
For collections you should aim to use IEnumerable where possible. This gives the most flexibility but is not always suited.
If you're working within a single method (or even in a single class or assembly in some cases) and no one outside is going to see what you're doing, use the fullness of a List. But if you're interacting with outside code, like when you're returning a list from a method, then you only want to declare the interface without necessarily tying yourself to a specific implementation, especially if you have no control over who compiles against your code afterward. If you started with a concrete type and you decided to change to another one, even if it uses the same interface, you're going to break someone else's code unless you started off with an interface or abstract base type.
You are most often better of using the most general usable type, in this case the IList or even better the IEnumerable interface, so that you can switch the implementation conveniently at a later time.
However, in .NET 2.0, there is an annoying thing - IList does not have a Sort() method. You can use a supplied adapter instead:
ArrayList.Adapter(list).Sort()
I don't think there are hard and fast rules for this type of thing, but I usually go by the guideline of using the lightest possible way until absolutely necessary.
For example, let's say you have a Person class and a Group class. A Group instance has many people, so a List here would make sense. When I declare the list object in Group I will use an IList<Person> and instantiate it as a List.
public class Group {
private IList<Person> people;
public Group() {
this.people = new List<Person>();
}
}
And, if you don't even need everything in IList you can always use IEnumerable too. With modern compilers and processors, I don't think there is really any speed difference, so this is more just a matter of style.
You should use the interface only if you need it, e.g., if your list is casted to an IList implementation other than List. This is true when, for example, you use NHibernate, which casts ILists into an NHibernate bag object when retrieving data.
If List is the only implementation that you will ever use for a certain collection, feel free to declare it as a concrete List implementation.
In situations I usually come across, I rarely use IList directly.
Usually I just use it as an argument to a method
void ProcessArrayData(IList almostAnyTypeOfArray)
{
// Do some stuff with the IList array
}
This will allow me to do generic processing on almost any array in the .NET framework, unless it uses IEnumerable and not IList, which happens sometimes.
It really comes down to the kind of functionality you need. I'd suggest using the List class in most cases. IList is best for when you need to make a custom array that could have some very specific rules that you'd like to encapsulate within a collection so you don't repeat yourself, but still want .NET to recognize it as a list.
A List object allows you to create a list, add things to it, remove it, update it, index into it and etc. List is used whenever you just want a generic list where you specify object type in it and that's it.
IList on the other hand is an Interface. Basically, if you want to create your own custom List, say a list class called BookList, then you can use the Interface to give you basic methods and structure to your new class. IList is for when you want to create your own, special sub-class that implements List.
Another difference is:
IList is an Interface and cannot be instantiated. List is a class and can be instantiated. It means:
IList<string> list1 = new IList<string>(); // this is wrong, and won't compile
IList<string> list2 = new List<string>(); // this will compile
List<string> list3 = new List<string>(); // this will compile