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.

To illustrate, 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:

using System.Collections.ObjectModel;

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

}

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

using System.Collections.ObjectModel;
using System.Web.Mvc;

namespace Exercise1.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Numbers()
        {
            var numbers = new Series<int>();
            numbers.Add(2);
            numbers.Add(937);
            numbers.Add(49);
            numbers.Add(8);
            numbers.Add(64);

            return View();
        }
    }

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

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

using System.Collections.ObjectModel;
using System.Web.Mvc;

namespace Exercise1.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Numbers()
        {
            var numbers = new Series<int>();
            numbers.Add(2);
            numbers.Add(937);
            numbers.Add(49);
            numbers.Add(8);
            numbers.Add(64);

            ViewBag.Numbers = numbers;

            return View();
        }
    }

    public class Series<T> : Collection<T>
    {
    }
}
----------------------------------------------------------------------------------
@{
    ViewBag.Title = "Numbers";
    var nbrs = ViewBag.Numbers;
}

<h2>Numbers</h2>

<ul>
    @for (int i = 0; i < nbrs.Count; i++)
    {
        <li>@nbrs[i]</li>
    }
</ul>

Collection

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

Here are examples of calling these methods:

@{
    ViewBag.Title = "Collection";
    System.Collections.ObjectModel.Collection<int> numbers =
            new System.Collections.ObjectModel.Collection<int>();

    numbers.Add(288);
    numbers.Add(1450);
    numbers.Add(46);
}

<h2>Collection</h2>

<p>Collection of Numbers</p>
<ul>
    @foreach (int nbr in numbers)
    {
        <li>@nbr</li>
    }
</ul>
<hr />
@{ numbers.Add(355); }
<p>After adding 355</p>

<ul>
    @foreach (int nbr in numbers)
    {
        <li>@nbr</li>
    }
</ul>
<hr />
@{ numbers.Insert(1, 25); }
<p>After inserting 25 in the second position</p>

<ul>
    @foreach (int nbr in numbers)
    {
        <li>@nbr</li>
    }
</ul>
<hr />
<p>The index of 1450 is @numbers.IndexOf(1450).</p>
<hr />
@{ numbers.RemoveAt(1); }
<p>After removing the second item...</p>

<ul>
    @foreach (int nbr in numbers)
    {
        <li>@nbr</li>
    }
</ul>
<hr />
@{ numbers.Clear(); }
<p>After clearing the list...</p>

@if (numbers.Count > 0)
{
    <ul>
        @foreach (int nbr in numbers)
        {
            <li>@nbr</li>
        }
    </ul>
}
else
{
    <p>Ain't nothing to see here.</p>
}

This would produce:

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.ObjectModel;

namespace Exercise1.Models
{
    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:

using System.Collections.ObjectModel;
using System.Web.Mvc;

namespace Exercise1.Controllers
{
    public class CollectorController : Controller
    {
        // GET: Collector
        public ActionResult Index()
        {
            return View();
        }

        public ActionResult Numbers()
        {
            var numbers = new Series<int>();
            numbers.Add(2);
            numbers.Add(937);
            numbers.Add(49);
            numbers.Add(8);
            numbers.Add(64);

            ViewBag.Numbers = numbers;

            return View();
        }

        public class Series<T> : Collection<T>
        {
        }
    }
}
----------------------------------------
@{
    ViewBag.Title = "Numbers";
    var nbrs = ViewBag.Numbers;
}

<h2>List of Numbers</h2>

<ul>
    @for (int i = 0; i < nbrs.Count; i++)
    {
        <li>@nbrs[i]</li>
    }
</ul>

<hr />
<h2>List of Integers</h2>

@{
    System.Collections.ObjectModel.Collection<int> integers = new System.Collections.ObjectModel.Collection<int>(nbrs);
}

<ul>
    @for (int i = 0; i < integers.Count; i++)
    {
        <li>@integers[i]</li>
    }
</ul>

This would produce:

Making a Copy of an Object Model Collection

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:

@{
    ViewBag.Title = "Numbers";
    var nbrs = ViewBag.Numbers;

    System.Collections.ObjectModel.Collection<int> integers =
            new System.Collections.ObjectModel.Collection<int>(nbrs);
}

<h2>List of Numbers</h2>

<ul>
    @for (int i = 0; i < nbrs.Count; i++)
    {
        <li>@nbrs[i]</li>
    }
</ul>

<h2>List of Integers</h2>

@{
    integers.Add(3049);
    integers.Add(58);
}

<hr />

<ul>
    @for (int i = 0; i < integers.Count; i++)
    {
        <li>@integers[i]</li>
    }
</ul>

This would produce:

Adding New Items a Copied Object Model Collection

Of course, you can also perform all the other allowed operations on the new collection. For example, you can add a property that produces a list held in the class. Here is an example:

using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace Exercise1.Models
{
    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:

using System.Collections.ObjectModel;
using System.Web.Mvc;

namespace Exercise1.Controllers
{
    public class CollectorController : Controller
    {
        // GET: Collector
        public ActionResult Index()
        {
            return View();
        }

        public ActionResult Numbers()
        {
            Series<int> numbers = new Series<int>();
            numbers.Add(2);
            numbers.Add(937);
            numbers.Add(49);
            numbers.Add(8);
            numbers.Add(64);

            numbers.InsertItem(3, 10508);
            ViewBag.Numbers = numbers;

            return View();
        }
    }

    public class Series<T> : Collection<T>
    {
        public new void InsertItem(int index, T item)
        {
            base.InsertItem(index, item);
        }
    }
}
---------------------------------------------------
@{
    ViewBag.Title = "Numbers";
    var nbrs = ViewBag.Numbers;
}

<h2>List of Numbers</h2>

<ul>
    @for (int i = 0; i < nbrs.Count; i++)
    {
        <li>@nbrs[i]</li>
    }
</ul>

Collection

If you use the default implementation of the class, if the user passes an invalid index, the compiler would throw an ArgumentOutOfRangeException exception. Consider the following example:

using System.Collections.ObjectModel;
using System.Web.Mvc;

namespace Exercise1.Controllers
{
    public class CollectorController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }

        public ActionResult Numbers()
        {
            Series<int> numbers = new Series<int>();
            numbers.Add(2);
            numbers.Add(937);
            numbers.Add(49);
            numbers.Add(8);
            numbers.Add(64);

            numbers.InsertItem(3, 10508);

            numbers.InsertItem(10, 9380475);

            ViewBag.Numbers = numbers;

            return View();
        }
    }

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

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

This would produce:

Inserting an Item in an Object Model Collection

To avoid this type of problem, one possible solution is to indicate to the compiler what to do in case of a problem. Here is an example:

public class Series<T> : Collection<T>
{
    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.Web.Mvc;
using System.Collections.ObjectModel;

namespace Exercise1.Controllers
{
    public class CollectorController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }

        public ActionResult Numbers()
        {
            Series<int> numbers = new Series<int>();
            numbers.Add(2);
            numbers.Add(937);
            numbers.Add(49);
            numbers.Add(8);
            numbers.Add(64);

            numbers.InsertItem(3, 10508);
            numbers.InsertItem(10, 9380475);

            ViewBag.Numbers = numbers;

            return View();
        }
    }

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

        public new void InsertItem(int index, T item)
        {
            if (index < 0)
                throw new ArgumentOutOfRangeException("The value you provided " +
                                                      "for the index is not valid");
            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);
        }
    }
}

This would produce:

Inserting an Item in an Object Model Collection

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 of 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:

using System;
using System.Web.Mvc;
using System.Collections.ObjectModel;

namespace Exercise1.Controllers
{
    public class CollectorController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }

        public ActionResult Numbers()
        {
            Series<int> numbers = new Series<int>();
            numbers.Add(2);
            numbers.Add(937);
            numbers.Add(49);
            numbers.Add(8);
            numbers.Add(64);

            numbers.InsertItem(3, 10508);
            numbers.InsertItem(10, 9380475);
            
            ViewBag.Numbers = numbers;

            return View();
        }
    }

    public class Series<T> : Collection<T>
    {
        public new void InsertItem(int index, T item)
        {
            if (index < 0)
                throw new ArgumentOutOfRangeException("The value you provided " +
                                                      "for the index is not valid");
            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)
        {
            base.SetItem(index, item);
        }
    }
}
----------------------------------------------------
@{
    ViewBag.Title = "Numbers";
    var nbrs = ViewBag.Numbers as System.Collections.ObjectModel.Collection<int>;
}

<h2>List of Numbers</h2>
<ul>
    @for (int i = 0; i < nbrs.Count; i++)
    {
        <li>@nbrs[i]</li>
    }
</ul>
<hr />

@{ ViewBag.Numbers.SetItem(2, 13579); }

<h2>List of Numbers</h2>

<ul>

This would produce:

Setting an Item in an Object Collection

This time too, if you decide to use the default implementation of the System.Collections.ObjectModel.SetItem() method, if you provide an index outside the number of items in the collection, the compiler would throw an IndexOutRangeException exception. To address such an issue, you provide a customized version of the method. This can be done as follows:

using System;
using System.Web.Mvc;
using System.Collections.ObjectModel;

namespace Exercise1.Controllers
{
    public class CollectorController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }

        public ActionResult Numbers()
        {
            Series<int> numbers = new Series<int>();
            numbers.Add(2);
            numbers.Add(937);
            numbers.Add(49);
            numbers.Add(8);
            numbers.Add(64);

            numbers.InsertItem(3, 10508);
            numbers.InsertItem(10, 9380475);
            
            ViewBag.Numbers = numbers;

            return View();
        }
    }

    public class Series<T> : Collection<T>
    {
        public new void InsertItem(int index, T item)
        {
            if (index < 0)
                throw new ArgumentOutOfRangeException("The value you provided " +
                                                      "for the index is not valid");
            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
                this[index] = item;

            // This too would work
            // 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. Here is an example of how this can be done:

using System;
using System.Web.Mvc;
using System.Collections.ObjectModel;

namespace Exercise1.Controllers
{
    public class CollectorController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }

        public ActionResult Numbers()
        {
            Series<int> numbers = new Series<int>();
            numbers.Add(2);
            numbers.Add(937);
            numbers.Add(49);
            numbers.Add(8);
            numbers.Add(64);

            numbers.InsertItem(3, 10508);
            numbers.InsertItem(10, 9380475);
            
            ViewBag.Numbers = numbers;

            return View();
        }
    }

    public class Series<T> : Collection<T>
    {
        public new void InsertItem(int index, T item)
        {
            if (index < 0)
                throw new ArgumentOutOfRangeException("The value you provided " +
                                                      "for the index is not valid");
            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
                this[index] = item;

            // This too would work
            // base.SetItem(index, item);
        }

        public new void RemoveItem(int index)
        {
            if (index < 0)
                return;
            else if (index > Count)
                return;
            else
                base.RemoveItem(index);
        }
    }
}
--------------------------------------------
@{
    ViewBag.Title = "Numbers";
    var nbrs = ViewBag.Numbers as System.Collections.ObjectModel.Collection<int>;
}

<h2>List of Numbers</h2>
<ul>
    @for (int i = 0; i < nbrs.Count; i++)
    {
        <li>@nbrs[i]</li>
    }
</ul>
<hr />

@{ ViewBag.Numbers.RemoveItem(4); }

<h2>List of Numbers</h2>

<ul>
    @for (int i = 0; i < ViewBag.Numbers.Count; i++)
    {
        <li>@ViewBag.Numbers[i]</li>
    }
</ul>

This would produce:

Removing an Item from an Object Model Collection

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. This can be done as follows:

Otherwise, define the method as you wish.

using System;
using System.Web.Mvc;
using System.Collections.ObjectModel;

namespace Exercise1.Controllers
{
    public class CollectorController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }

        public ActionResult Numbers()
        {
            Series<int> numbers = new Series<int>();
            numbers.Add(2);
            numbers.Add(937);
            numbers.Add(49);
            numbers.Add(8);
            numbers.Add(64);

            numbers.InsertItem(3, 10508);
            numbers.InsertItem(10, 9380475);
            
            ViewBag.Numbers = numbers;

            return View();
        }
    }

    public class Series<T> : Collection<T>
    {
        public new void InsertItem(int index, T item)
        {
            if (index < 0)
                throw new ArgumentOutOfRangeException("The value you provided " +
                                                      "for the index is not valid");
            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
                this[index] = item;

            // This too would work
            // base.SetItem(index, item);
        }

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

        public new void Clear()
        {
            base.Clear();
        }
    }
}
------------------------------------------------
@{
    ViewBag.Title = "Numbers";
    var nbrs = ViewBag.Numbers as System.Collections.ObjectModel.Collection<int>;
}

<h2>List of Numbers</h2>
<ul>
    @for (int i = 0; i < nbrs.Count; i++)
    {
        <li>@nbrs[i]</li>
    }
</ul>
<hr />

@{ ViewBag.Numbers.RemoveItem(4); }

<h2>List of Numbers</h2>

<ul>
    @for (int i = 0; i < ViewBag.Numbers.Count; i++)
    {
        <li>@ViewBag.Numbers[i]</li>
    }
</ul>
<hr />

@{ ViewBag.Numbers.Clear(); }

<h2>List of Numbers</h2>

<ul>
    @for (int i = 0; i < ViewBag.Numbers.Count; i++)
    {
        <li>@ViewBag.Numbers[i]</li>
    }
</ul>

This would produce:

Collection


Previous Copyright © 2004-2019, FunctionX Next