Home

The Collection Class

   

Fundamentals of the Collection Class

 

Introduction

Besides the System.Collections and the System.Collections.Generic namespaces, the .NET Framework provides additional collection classes in various other namespaces. One of those namespaces is System.Collections.ObjectModel. That namespace provides techniques of creating collections that emanate from other, existing, collections. For example, suppose you have a collection already but have a particular scenario in which you want to use that collection. Instead of creating a new collection, you can simply transfer the existing values to the new collection.

To state it another way, or to make it a little clearer, suppose you have a list of students whose records must be passed to a review. You can create another list and transfer the students records to that new list but block it, that is, make it read-only so that whoever accesses those records cannot change them. As another scenario, suppose you have a list of employees from one company that is merging with another company, you can easily transfer the existing employees records to a new list (for whatever reason) without creating a new collection. You can then modify the new collection from the existing records.

The System.Collections.ObjectModel namespace provides (two) abstract classes and other additional classes. The abstraction classes are named Collection and KeyedCollection.

Using the Collection Class

The Collection<> class is one of the semi-complete classes of the .NET Framework. It is announced as follows:

public class Collection<T> : IList<T>, 
    			     ICollection<T>,
    			     IEnumerable<T>,
    			     IList, ICollection,
    			     IEnumerable

As you can see, this is primarily a normal collection class that implements the IList<>, the ICollection<>, and the IEnumerable<> interfaces. This makes it possible to add items to a collection and to manage the list. Normally, you can use the Collection<> class "as is". It is equipped with all the regular properties and methods.

The Collection<> class is mostly useful if you want to create a class that impliments some particular behavior you want, in which case you would first create a class based on Collection<>. Here is an example:

// References Needed: System.dll,
//                    System.Drawing,
//                    System.Windows.Form
using System;
using System.Windows.Forms;
using System.Collections.ObjectModel;

public class Algebra : Form
{
    public Algebra()
    {
        InitializeComponent();
    }

    private void InitializeComponent()
    {
        Text = "Algebra";
        StartPosition = FormStartPosition.CenterScreen;
    }
}

public class Series<T> : Collection<T>
{
}

public class Exercise
{
    public static int Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new Algebra());

        return 0;
    }
}

Remember that the Collection<> class receives the ability to add a new item from the ICollection interface. Here are examples of calling it:

 private void btnNumbersClicked(object sender, EventArgs e)
{
    numbers = new Series<int>();
    numbers.Add(2);
    numbers.Add(937);
    numbers.Add(49);
    numbers.Add(8);
    numbers.Add(64);
}

Remember that the Collection<> class inherits the Item and the Count properties. Here are examples of using them:

// References Needed: System.dll,
//                    System.Drawing,
//                    System.Windows.Form
using System;
using System.Windows.Forms;
using System.Collections.ObjectModel;

public class Algebra : Form
{
    Button btnNumbers;
    ListBox lbxNumbers;
    Series<int> numbers;

    public Algebra()
    {
        InitializeComponent();
    }

    private void InitializeComponent()
    {
        btnNumbers = new Button();
        btnNumbers.Text = "Numbers";
        btnNumbers.Location = new System.Drawing.Point(12, 12);
        btnNumbers.Click += new EventHandler(btnNumbersClicked);

        lbxNumbers = new ListBox();
        lbxNumbers.Location = new System.Drawing.Point(12, 44);

        Text = "Algebra";
        Size = new System.Drawing.Size(150, 180);
        StartPosition = FormStartPosition.CenterScreen;

        Controls.Add(btnNumbers);
        Controls.Add(lbxNumbers);
    }

    private void btnNumbersClicked(object sender, EventArgs e)
    {
        numbers = new Series<int>();
        numbers.Add(2);
        numbers.Add(937);
        numbers.Add(49);
        numbers.Add(8);
        numbers.Add(64);

        for(int i = 0; i < numbers.Count; i++)
            lbxNumbers.Items.Add(numbers[i]);
    }
}

public class Series<T> : Collection<T>
{
}

public class Exercise
{
    public static int Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new Algebra());

        return 0;
    }
}

Collection

As an inheritor of the ICollection<> interface, the Collection<> class has functionalities:

  • To insert a new item inside the collection
  • To get the index of an existing item
  • check the availability of an item (using the Contains() method)
  • To delete an item from the collection by calling either the Remove() or the RemoveAt() method
  • To remove all items from the collection by calling the Clear() method

Here are example of calling these methods (in the following examples, to make code easier, there was no error checking and exception handling was ignored):

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Collections.ObjectModel;

namespace Algebra3
{
    public partial class Exercise : Form
    {
        Collection<int> numbers;

        public Exercise()
        {
            InitializeComponent();
        }

        private void Exercise_Load(object sender, EventArgs e)
        {
            numbers = new Collection<int>();
        }

        private void FillListBox()
        {
            lbxNumbers.Items.Clear();

            foreach(int nbr in numbers)
                lbxNumbers.Items.Add(nbr);
        }

        private void btnAdd_Click(object sender, EventArgs e)
        {
            numbers.Add(int.Parse(txtNumber.Text));

            txtNumber.Text = "";
            txtNumber.Focus();

            FillListBox();
        }

        private void btnInsert_Click(object sender, EventArgs e)
        {
            numbers.Insert(int.Parse(txtIndex.Text), int.Parse(txtNumber.Text));

            txtNumber.Text = "";
            txtIndex.Text = "";
            txtNumber.Focus();

            FillListBox();
        }

        private void btnCheckIndexOf_Click(object sender, EventArgs e)
        {
            if (numbers.Contains(int.Parse(txtIndexOf.Text)) )
                MessageBox.Show("The index of " + txtIndexOf.Text +
                                " is " + numbers.IndexOf(int.Parse(txtIndexOf.Text)),
                                "Algebra", MessageBoxButtons.OK,
                                MessageBoxIcon.Information);
            else
          MessageBox.Show("The collection doesn't contain " + txtIndexOf.Text + ". ",
                                "Algebra", MessageBoxButtons.OK,
                                MessageBoxIcon.Information);
        }

        private void btnRemove_Click(object sender, EventArgs e)
        {
            numbers.Remove(int.Parse(txtRemove.Text));

            FillListBox();
        }

        private void btnRemoveAllNumbers_Click(object sender, EventArgs e)
        {
            numbers.Clear();

            FillListBox();
        }

        private void btnClose_Click(object sender, EventArgs e)
        {
            Close();
        }
    }
}
Collection Collection
Collection Collection
 
 
 

Creating a Collection<> Class

 

Introduction

As stated already, the real role of the Collection<> class is to let you derive a class from it and implement some custom behaviors. To let you do this, the class is equipped with a property and some methods that are not inherited from ICollection<>. Because these are protected members, you must override them in your own class.

You can start the class as follows:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;

namespace CeilInn1
{
    [Serializable]
    public class RoomManagement<T> : Collection<T>
    {
    	public RoomManagement() : base()
	{
	}
    }
}

Copying a Collection

The Collection<> class has two constructors. The default is used to create an empty list. The other constructor uses the following syntax:

public Collection(IList<T> list);

This constructor allows you to create a list based on another existing list that is from a class that implements the IList<> interface. Here is an example of using this constructor:

// References Needed: System.dll,
//                    System.Drawing,
//                    System.Windows.Form
using System;
using System.Windows.Forms;
using System.Collections.ObjectModel;

public class Algebra : Form
{
    Button btnNumbers;
    Button btnIntegers;
    ListBox lbxNumbers;
    Series<int> numbers;

    public Algebra()
    {
        InitializeComponent();
    }

    private void InitializeComponent()
    {
        btnNumbers = new Button();
        btnNumbers.Text = "Numbers";
        btnNumbers.Location = new System.Drawing.Point(12, 12);
        btnNumbers.Click += new EventHandler(btnNumbersClicked);

        btnIntegers = new Button();
        btnIntegers.Text = "New Numbers";
        btnIntegers.Width = 90;
        btnIntegers.Location = new System.Drawing.Point(90, 12);
        btnIntegers.Click += new EventHandler(btnIntegersClicked);

        lbxNumbers = new ListBox();
        lbxNumbers.Location = new System.Drawing.Point(40, 44);

        Text = "Algebra";
        Size = new System.Drawing.Size(200, 180);
        StartPosition = FormStartPosition.CenterScreen;

        Controls.Add(btnNumbers);
        Controls.Add(lbxNumbers);
        Controls.Add(btnIntegers);
    }

    private void btnNumbersClicked(object sender, EventArgs e)
    {
        numbers = new Series<int>();
        numbers.Add(2);
        numbers.Add(937);
        numbers.Add(49);
        numbers.Add(8);
        numbers.Add(64);

        for(int i = 0; i < numbers.Count; i++)
            lbxNumbers.Items.Add(numbers[i]);
    }

    private void btnIntegersClicked(object sender, EventArgs e)
    {
        Collection<int> integers = new Collection<int>(numbers);

        lbxNumbers.Items.Clear();

        for (int i = 0; i < integers.Count; i++)
            lbxNumbers.Items.Add(integers[i]);
    }
}

public class Series<T> : Collection<T>
{
}

On the other hand, if you had previously created a Collection<> object, to let you get it as an IList<>, the Collection<> class provides a property named Items that of type IList<>:

protected IList<T> Items { get; }

As you can see, Items is a protected property. This means that, to use it, you must first create a class that overrides it.

In both cases, whether using the Collection(IList<T> list) constructor or the Items property to get a new list, once you have the new collection, you can use it as you see fit. For example, you can add new values to it. Here are examples:

// References Needed: System.dll,
//                    System.Drawing,
//                    System.Windows.Form
using System;
using System.Windows.Forms;
using System.Collections.Generic;
using System.Collections.ObjectModel;

public class Algebra : Form
{
    Button btnNumbers;
    Button btnIntegers;
    ListBox lbxNumbers;
    Series<int> numbers;

    public Algebra()
    {
        InitializeComponent();
    }

    private void InitializeComponent()
    {
        btnNumbers = new Button();
        btnNumbers.Text = "Numbers";
        btnNumbers.Location = new System.Drawing.Point(12, 12);
        btnNumbers.Click += new EventHandler(btnNumbersClicked);

        btnIntegers = new Button();
        btnIntegers.Text = "New Numbers";
        btnIntegers.Width = 90;
        btnIntegers.Location = new System.Drawing.Point(90, 12);
        btnIntegers.Click += new EventHandler(btnIntegersClicked);

        lbxNumbers = new ListBox();
        lbxNumbers.Location = new System.Drawing.Point(40, 44);

        Text = "Algebra";
        Size = new System.Drawing.Size(200, 180);
        StartPosition = FormStartPosition.CenterScreen;

        Controls.Add(btnNumbers);
        Controls.Add(lbxNumbers);
        Controls.Add(btnIntegers);
    }

    private void btnNumbersClicked(object sender, EventArgs e)
    {
        numbers = new Series<int>();
        numbers.Add(2);
        numbers.Add(937);
        numbers.Add(49);
        numbers.Add(8);
        numbers.Add(64);

        for(int i = 0; i < numbers.Count; i++)
            lbxNumbers.Items.Add(numbers[i]);
    }

    private void btnIntegersClicked(object sender, EventArgs e)
    {
        Series<int> integers = (Series<int>)numbers.Items;

        integers.Add(1865);
        integers.Add(370);

        lbxNumbers.Items.Clear();

        for (int i = 0; i < integers.Count; i++)
            lbxNumbers.Items.Add(integers[i]);
    }
}

public class Series<T> : Collection<T>
{
    public IList<T> Items
    {
        get
        {
            return this;
        }
    }
}

Collection  Collection

Of course, you can also perform all the other allowed operations on the new collection.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;

namespace CeilInn1
{
    [Serializable]
    public class RoomManagement<T> : Collection<T>
    {
    	public RoomManagement() : base()
	{
	}

        public RoomManagement(IList<T> list) : base(list)
        {
        }

        public virtual new IList<T> Items
        {
            get
            {
                return this;
            }
        }
    }
}

Inserting an Item

To let you customize the way an item should be inserted in a list, the Collection<> class provides the InsertItem() method. Its syntax is:

protected virtual void InsertItem(int index, T item);

This method takes two arguments: the item that will be inserted and the position it must occupy. Once again, the easisest implementation would consist of calling the parent's definition, which should be fine in a regular scenario. Here is an example:

// References Needed: System.dll,
//                    System.Drawing,
//                    System.Windows.Form
using System;
using System.Windows.Forms;
using System.Collections.Generic;
using System.Collections.ObjectModel;

public class Algebra : Form
{
    Button btnNumbers;
    Button btnInsert;
    ListBox lbxNumbers;
    Series<int> numbers;

    public Algebra()
    {
        InitializeComponent();
    }

    private void InitializeComponent()
    {
        btnNumbers = new Button();
        btnNumbers.Text = "Numbers";
        btnNumbers.Location = new System.Drawing.Point(12, 12);
        btnNumbers.Click += new EventHandler(btnNumbersClicked);

        btnInsert = new Button();
        btnInsert.Text = "Insert";
        btnInsert.Location = new System.Drawing.Point(90, 12);
        btnInsert.Click += new EventHandler(btnInsertClicked);

        lbxNumbers = new ListBox();
        lbxNumbers.Location = new System.Drawing.Point(40, 44);

        Text = "Algebra";
        Size = new System.Drawing.Size(200, 180);
        StartPosition = FormStartPosition.CenterScreen;

        Controls.Add(btnNumbers);
        Controls.Add(lbxNumbers);
        Controls.Add(btnInsert);
    }

    private void btnNumbersClicked(object sender, EventArgs e)
    {
        numbers = new Series<int>();
        numbers.Add(2);
        numbers.Add(937);
        numbers.Add(49);
        numbers.Add(8);
        numbers.Add(64);

        for(int i = 0; i < numbers.Count; i++)
            lbxNumbers.Items.Add(numbers[i]);
    }

    private void btnInsertClicked(object sender, EventArgs e)
    {
        numbers.InsertItem(3, 10509);

        lbxNumbers.Items.Clear();

        for (int i = 0; i < numbers.Count; i++)
            lbxNumbers.Items.Add(numbers[i]);
    }
}

public class Series<T> : Collection<T>
{
    public new IList<T> Items
    {
        get
        {
            return this;
        }
    }

    public new void SetItem(int index, T item)
    {
        if (index < 0)
            return;
        else if (index > Count)
            return;
        else
            this[index] = item;
        
        // This too would work
        // base.SetItem(index, item);
    }

    public new void InsertItem(int index, T item)
    {
        base.InsertItem(index, item);
    }
}

Collection  Collection

An alternative is to create your own implementation. For example, if the user passes an invalid index, the compiler would throw an ArgumentOutOfRangeException exception. One probable solution is to indicate to the compiler what to do in case of a problem. Here is an example:

public new void InsertItem(int index, T item)
{
    if( (index >= 0) && (index <= Count) )
        base.InsertItem(index, item);
}

Of course, another solution is to call the ICollection<>.Insert() method.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;

namespace CeilInn1
{
    [Serializable]
    public class RoomManagement<T> : Collection<T>
    {
        public RoomManagement() : base()
        {
        }

        public RoomManagement(IList<T> list) : base(list)
        {
        }

        public virtual new IList<T> Items
        {
            get
            {
                return this;
            }
        }

        public new void InsertItem(int index, T item)
        {
            if (index < 0)
                throw new ArgumentOutOfRangeException("The value you provided " +
                                                      "for the index is not valid");
            else if ((index >= 0) && (index <= Count))
                base.InsertItem(index, item);
            else
   // If the index is higher than Count, simply add the item (at the end of the list)
                this.Add(item);
        }
    }
}

Setting an Item

The Collection<> class implements the ICollection interface. This allows you to access an item using its index. Besides the ability to specify the value an item based on its index, the Collection<> class provides the SetItem() method that makes it possible to change an item. Its syntax is:

protected virtual void SetItem(int index, T item);

The item argument holds the value that will be assigned to the item at the index position. When overriding this method, the simplest implementation would consist of calling the same method of the parent class. Here is an example:

// References Needed: System.dll,
//                    System.Drawing,
//                    System.Windows.Form
using System;
using System.Windows.Forms;
using System.Collections.Generic;
using System.Collections.ObjectModel;

public class Algebra : Form
{
    Button btnNumbers;
    Button btnChange;
    ListBox lbxNumbers;
    Series<int> numbers;

    public Algebra()
    {
        InitializeComponent();
    }

    private void InitializeComponent()
    {
        btnNumbers = new Button();
        btnNumbers.Text = "Numbers";
        btnNumbers.Location = new System.Drawing.Point(12, 12);
        btnNumbers.Click += new EventHandler(btnNumbersClicked);

        btnChange = new Button();
        btnChange.Text = "New Numbers";
        btnChange.Width = 90;
        btnChange.Location = new System.Drawing.Point(90, 12);
        btnChange.Click += new EventHandler(btnChangeClicked);

        lbxNumbers = new ListBox();
        lbxNumbers.Location = new System.Drawing.Point(40, 44);

        Text = "Algebra";
        Size = new System.Drawing.Size(200, 180);
        StartPosition = FormStartPosition.CenterScreen;

        Controls.Add(btnNumbers);
        Controls.Add(lbxNumbers);
        Controls.Add(btnChange);
    }

    private void btnNumbersClicked(object sender, EventArgs e)
    {
        numbers = new Series<int>();
        numbers.Add(2);
        numbers.Add(937);
        numbers.Add(49);
        numbers.Add(8);
        numbers.Add(64);

        for(int i = 0; i < numbers.Count; i++)
            lbxNumbers.Items.Add(numbers[i]);
    }

    private void btnChangeClicked(object sender, EventArgs e)
    {
        numbers.SetItem(2, 13579);

        lbxNumbers.Items.Clear();

        for (int i = 0; i < numbers.Count; i++)
            lbxNumbers.Items.Add(numbers[i]);
    }
}

public class Series<T> : Collection<T>
{
    public new IList<T> Items
    {
        get
        {
            return this;
        }
    }

    public new void SetItem(int index, T item)
    {
        base.SetItem(index, item);
    }
}

Collection  Collection

Otherwise, when defining this method, you must decide what to do in case of this or that. For example, what should the compiler do if the index is negative? What if the passed index is higher than the total number of items in the collection. In this case, by default, the compiler would throw an IndexOutRangeException exception. One alternative is to ignore the new value if the index is out of range. Here is an example of implementing:

// References Needed: System.dll,
//                    System.Drawing,
//                    System.Windows.Form
using System;
using System.Windows.Forms;
using System.Collections.Generic;
using System.Collections.ObjectModel;

public class Algebra : Form
{
    Button btnNumbers;
    Button btnChange;
    ListBox lbxNumbers;
    Series<int> numbers;

    . . .

    private void btnChangeClicked(object sender, EventArgs e)
    {
        numbers.SetItem(12, 13579);

        lbxNumbers.Items.Clear();

        for (int i = 0; i < numbers.Count; i++)
            lbxNumbers.Items.Add(numbers[i]);
    }
}

public class Series<T> : Collection<T>
{
    public new IList<T> Items
    {
        get
        {
            return this;
        }
    }

    public new void SetItem(int index, T item)
    {
        if (index < 0)
            return;
        else if (index > Count)
            return;
        else
            this[index] = item;
        
        // This too would work
        // base.SetItem(index, item);
    }
}

Another alternative is to add the item as the last in the list.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;

namespace CeilInn2
{
    [Serializable]
    public class RoomManagement<T> : Collection<T>
    {
        public RoomManagement() : base()
        {
        }

        public RoomManagement(IList<T> list) : base(list)
        {
        }

        public virtual new IList<T> Items
        {
            get
            {
                return this;
            }
        }

        public new void InsertItem(int index, T item)
        {
            if (index < 0)
                throw new ArgumentOutOfRangeException("The value you provided " +
                                                      "for the index is not valid");
            else if ((index >= 0) && (index <= Count))
                base.InsertItem(index, item);
            else
   // If the index is higher than Count, simply add the item (at the end of the list)
                this.Add(item);
        }

        public new void SetItem(int index, T item)
        {
            if (index < 0)
                return;
            else if (index > Count)
                return;
            else
                base.SetItem(index, item);
        }
    }
}

Deleting an Item

To let you delete an item from its list, the Collection<> class provides the RemoveItem() method. Its syntax is:

protected virtual void RemoveItem(int index);

This method takes as argument the index of the item to be removed. Once again, a simply implementation would consist of calling the base method. Here is an example;

public new void RemoveItem(int index)
{
    base.RemoveItem(index);
}

An alternative that produces the same effect is to call the ICollection<>.RemoveAt() method. Here is an example:

public new void RemoveItem(int index)
{
    base.RemoveAt(index);
}

Otherwise, when implementing this method, you will decide what to do if the index is negative or is higher than the total number of items in the list. Also, you can specify what other actions the compiler should take when an item is being deleted.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;

namespace CeilInn2
{
    [Serializable]
    public class RoomManagement<T> : Collection<T>
    {
        public RoomManagement() : base()
        {
        }

        public RoomManagement(IList<T> list) : base(list)
        {
        }

        public virtual new IList<T> Items
        {
            get
            {
                return this;
            }
        }

        public new void InsertItem(int index, T item)
        {
            if (index < 0)
                throw new ArgumentOutOfRangeException("The value you provided " +
                                                      "for the index is not valid");
            else if ((index >= 0) && (index <= Count))
                base.InsertItem(index, item);
            else
   // If the index is higher than Count, simply add the item (at the end of the list)
                this.Add(item);
        }

        public new void SetItem(int index, T item)
        {
            if (index < 0)
                return;
            else if (index > Count)
                return;
            else
                base.SetItem(index, item);
        }

        public new void RemoveItem(int index)
        {
            if (index < 0)
                return;
            else if (index > Count)
                return;
            else
                base.RemoveItem(index);
        }
    }
}

Clearing the Collection

Because the Collection<> class implements the ICollection interface, it inherits the Clear() method. To let you create a custom technique to delete all items from a class, the Collection class is equipped with the protected ClearItems() method. Its syntax is:

protected virtual void ClearItems();

When overriding this method, if you don't have any particular way you want to delete items, you can simply call the Clear() method. Otherwise, define the method as you wish.

 
 
   
 

Home Copyright © 2014, FunctionX