Thursday, February 22, 2007

ICollection<T>, IList<T>, CollectionBase, Collection<T> And List<T>

.NET provides all these interfaces and classes. What is the difference between them?

They all inherit/implement IEnumerable interface, and support simple iteration over the collection.

ICollection<T> interface is the simplest one. It only inherits IEnumerable interface and includes just a few members.

IList<T> interface inherits ICollection<T> interface, and it offers you the ability to index an element. So go IList<T> if you need indexing.

CollectionBase is an abstract class inheriting from ICollection and IList interfaces. It's a recommanded way of using it to implement a custom collection before .NET 2.0, derive a class from it and you get all functions implemented by CollectionBase.

Collection<T> is a concrete implementation of ICollection<T> and IList<T>. It's better than CollectionBase because it's a generic collection and supports all new generic features.

List<T> class also implements both ICollection<T> and IList<T>. It has more power than Collection<T>. Collection<T> exposes about 10 methods, while List<T> exposes more than 40 methods. You are able to do sorting and search by default with List<T>.

List<T> has more functions, but too much implementation limits its extensibility. In general, use Collection<T> for public APIs, and use List<T> for internal containers.

An example of Collection<T> more extensible than List<T> is that Collecton<T> has virtual methods so you can override them and have your own handling logic when an item is inserted and updated inside the container:
    protected virtual void ClearItems();
protected virtual void InsertItem(int index, T item);
protected virtual void RemoveItem(int index);
protected virtual void SetItem(int index, T item);
Some people argue that interface is better because it only provides the contract and the detailed implementation is not exposed, and returning IList<T> or ICollection<T> are better Collection<T> or List<T>, in addition you can not inherit your custom class once it deriving from concrete class like collection<T> or List<T>. That's true, but other people debate that returning interface without functions to end users is useless, what's the point you expose a collection of objects without some basic functions like search and sorting? That's also valid. It really depends on the real situation and use case.

Related interface/class definition:
    public interface IEnumerable
{
IEnumerator GetEnumerator();
}

public interface IEnumerable<T> : IEnumerable
{
IEnumerator<T> GetEnumerator();
}

public interface ICollection<T> : IEnumerable<T>, IEnumerable
{
bool IsReadOnly { get; }
void Add(T item);
void Clear();
bool Contains(T item);
void CopyTo(T[] array, int arrayIndex);
bool Remove(T item);
}

public interface IList<T> : ICollection<T>, IEnumerable<T>, IEnumerable
{
T this[int index] { get; set; }
int IndexOf(T item);
void Insert(int index, T item);
void RemoveAt(int index);
}

public abstract class CollectionBase : IList, ICollection, IEnumerable
{
protected CollectionBase();
protected CollectionBase(int capacity);
public int Capacity { get; set; }
public int Count { get; }
protected ArrayList InnerList { get; }
protected IList List { get; }
public void Clear();
public IEnumerator GetEnumerator();
protected virtual void OnClear();
protected virtual void OnClearComplete();
protected virtual void OnInsert(int index, object value);
protected virtual void OnInsertComplete(int index, object value);
protected virtual void OnRemove(int index, object value);
protected virtual void OnRemoveComplete(int index, object value);
protected virtual void OnSet(int index, object oldValue, object newValue);
protected virtual void OnSetComplete(int index, object oldValue, object newValue);
protected virtual void OnValidate(object value);
public void RemoveAt(int index);
}

public class Collection<T> : IList<T>, ICollection<T>, IEnumerable<T>, IList, ICollection, IEnumerable
{
public Collection();
public Collection(IList<T> list);
public int Count { get; }
protected IList<T> Items { get; }
public T this[int index] { get; set; }
public void Add(T item);
public void Clear();
protected virtual void ClearItems();
public bool Contains(T item);
public void CopyTo(T[] array, int index);
public IEnumerator<T> GetEnumerator();
public int IndexOf(T item);
public void Insert(int index, T item);
protected virtual void InsertItem(int index, T item);
public bool Remove(T item);
public void RemoveAt(int index);
protected virtual void RemoveItem(int index);
protected virtual void SetItem(int index, T item);
}

public class List<T> : IList<T>, ICollection<T>, IEnumerable<T>, IList, ICollection, IEnumerable
{
public List();
public List(IEnumerable<T> collection);
public List(int capacity);
public int Capacity { get; set; }
public int Count { get; }
public T this[int index] { get; set; }
public void Add(T item);
public void AddRange(IEnumerable<T> collection);
public ReadOnlyCollection<T> AsReadOnly();
public int BinarySearch(T item);
public int BinarySearch(T item, IComparer<T> comparer);
public int BinarySearch(int index, int count, T item, IComparer<T> comparer);
public void Clear();
public bool Contains(T item);
public List<TOutput> ConvertAll<TOutput>(Converter<T, TOutput> converter);
public void CopyTo(T[] array);
public void CopyTo(T[] array, int arrayIndex);
public void CopyTo(int index, T[] array, int arrayIndex, int count);
public bool Exists(Predicate<T> match);
public T Find(Predicate<T> match);
public List<T> FindAll(Predicate<T> match);
public int FindIndex(Predicate<T> match);
public int FindIndex(int startIndex, Predicate<T> match);
public int FindIndex(int startIndex, int count, Predicate<T> match);
public T FindLast(Predicate<T> match);
public int FindLastIndex(Predicate<T> match);
public int FindLastIndex(int startIndex, Predicate<T> match);
public int FindLastIndex(int startIndex, int count, Predicate<T> match);
public void ForEach(Action<T> action);
public List<T>.Enumerator GetEnumerator();
public List<T> GetRange(int index, int count);
public int IndexOf(T item);
public int IndexOf(T item, int index);
public int IndexOf(T item, int index, int count);
public void Insert(int index, T item);
public void InsertRange(int index, IEnumerable<T> collection);
public int LastIndexOf(T item);
public int LastIndexOf(T item, int index);
public int LastIndexOf(T item, int index, int count);
public bool Remove(T item);
public int RemoveAll(Predicate<T> match);
public void RemoveAt(int index);
public void RemoveRange(int index, int count);
public void Reverse();
public void Reverse(int index, int count);
public void Sort();
public void Sort(Comparison<T> comparison);
public void Sort(IComparer<T> comparer);
public void Sort(int index, int count, IComparer<T> comparer);
public T[] ToArray();
public void TrimExcess();
public bool TrueForAll(Predicate<T> match);

public struct Enumerator : IEnumerator<T>, IDisposable, IEnumerator
{
public T Current { get; }
public void Dispose();
public bool MoveNext();
}
}