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();
}
}

Friday, February 09, 2007

Binding Enum Type In .NET

Enum type is not bindable to list controls such as DropDownList and CheckBoxList, because Enum is not IEnumerable, IListSource or IDataSource type. One solution is put enum data to an IEnumerable collection such as generic dictionary, and then bind the list control to that collection. Following code demos such usage:
using System;
using System.Collections.Generic;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
BindableEnum.BindListControl<Status>(DropDownList1);
}
}

public enum Status
{
NotAvailable = 0,
Pending = 3,
InProgress = 4,
Completed = 5,
Aborted = 6
}

public static class BindableEnum
{
public static void BindListControl<TEnum>(ListControl list)
{
if (typeof(TEnum).BaseType != typeof(Enum))
return;

Dictionary<string, string> enumDict = new Dictionary<string, string>();
string[] names = Enum.GetNames(typeof(TEnum));
for (int i = 0; i < names.Length; i++)
{
enumDict.Add(names[i], ((int)Enum.Parse(typeof(TEnum), names[i])).ToString());
}
list.DataSource = enumDict;
list.DataTextField = "Key";
list.DataValueField = "Value";
list.DataBind();
}
}