Greetings to my blog readers!
Today I am feeling greedy. I want to create a collection class and I want it to do many-many things, including dancing and singing. I want it all! I want to be able to add items of any type to my collection. Thus, it has to be a generic collection, but I want it to be type-safe. I also want it to have iteration capability, so that I can access any value. Also, I want to have some removal functionality. And yes, I want it to dance and sing. Let’s code it up.
Below is my coveted collection class. Nice, huh?
public sealed class MyCollection<T> { public MyCollection(int s) { if (s>=0) this.storage = new T[s]; } public int MySize() { return storage.Length; } public void AddValueAt(T addItem, int pos) { if (pos<MySize()) storage[pos] = addItem; } public T GetValueAt(int i) { if (i < MySize()) return storage[i]; else return default(T); } public void RemoveValueAt(int pos) { if (pos < MySize()) { IEnumerable<T> query = storage.Where((value, index) => index != pos).ToArray(); this.storage = new T[MySize() - 1]; int newPos = 0; foreach (T q in query) { this.AddValueAt(q, newPos); newPos++; } } } public IEnumerator<T> GetEnumerator() { foreach (T item in storage) yield return item; } public void SingAndDance() { Console.WriteLine("Singing and dancing!"); } private T[] storage; }
MyCollection is a generic collection, meaning it can be used to store items of any type. I can create MyCollection of integers, strings, or even objects. The class has the required functionality to add new values to the ‘storage’ array, remove a value at some position, and I even override GetEnumerator() to allow users of MyCollection to use foreach loop.
As much as I like MyCollection, there is quite a bit that is wrong with it. Firstly, it is far from being a robust little collection. Even though I check for positions to be below the allocated array size, a line if code that says something like this will compile:
myColl.AddValueAt(2, -1);
Of course, I will get System.IndexOutOfRangeException at run time, but it would have been nice to catch little bugs like this at compile time. Also, my RemoveValueAt() is, at best, inefficient. Finally, I had to type all these lines of code just to have MyCollection class to carry out the basic functionality, and of course, to sing and dance too. There must be a better way to create collection classes in a rich programming language like C#. And there is a better way, and it involves inheritance. To be more specific, one should always make a custom collection class derive from System.Collections.ObjectModel.Collection. Doing so will provide loads of functionality to the custom collection for free, including: Add, Remove, RemoveAt, Clear, IndexOf, and many more. Take a look at the modified MyCollection class:
public sealed class MyCollection<T> : System.Collections.ObjectModel.Collection<T> { public void SingAndDance() { Console.WriteLine("Singing and dancing!"); } }
And that is it. All other functionality, like AddValueAt, RemoveValueAt, MySize and even the GetEnumerator is provided by the base class (albeit under slightly different method names). Although, the attempt to remove an item at a negative index would still compile.
To conclude, the moral of this post is – reuse, reuse, reuse. Also, check-out this nice set of blog posts on collections and generics.