Using IList and ICollection Interfaces

Discussion in 'C#' started by shabbir, Mar 10, 2014.

  1. shabbir

    shabbir Administrator Staff Member

    Joined:
    Jul 12, 2004
    Messages:
    15,375
    Likes Received:
    388
    Trophy Points:
    83
    In my last article, I explained, how can we iterate upon a collection to traverse or enumerate its items using the IEnumerable and IEnumerator interfaces along with their generic conterparts. However, those interfaces have some limitations, for instance we could only traverse the collection in forward direction using those interfaces; we could not get the count of collection neither we were able to get an element by specifying the index of the element. Apart from that we could not modify or search the collection. For these functionalities, .NET framework has some other framework including IList, ICollection & IDictionary interfaces. An interesting thing to note here is that unlike IEnumerator and IEnumerable interfaces; the IList <T> and ICollection<T> interface do not inherit from their non-generic counterparts. In fact in most of the cases, IList and ICollection interfaces are used only for legacy systems that still use non-generic collections. In this article, we will explain what the declarations of these interfaces are and how these interfaces can be implemented in programs.

    First, let us briefly distinguish between IList<T>, ICollection<T> and IEnumerable <T> interfaces. IEnumerable<T> and IEnumerable interfaces provides the basic functionality, or in other words, the minimum functionality. It allows you to enumerate and traverse elements of a collection. ICollection<T> and ICollection interfaces slightly enhance the capabilities of IEnumerable interfaces and some other functionality such as counting the numbers in a collection etc. IList <T> (also IList) and IDictionary<T> (also IDictionary) are the most advanced of the aforementioned interfaces. These interfaces provide the ability to search an element in the collection based on index or the key.
    1. ICollection<T> and ICollection Interfaces
    2. IList<T> and IList Interfaces

    ICollection<T> and ICollection Interfaces



    The ICollection<T> along with its non-generic counterpart provides medium level capabilities on collections which are as follows.
    1. Count - Count the elements in the collection).
    2. Contains - Determines if specified element exits in the collection).
    3. ToArray - Converts the collection into array).
    4. IsReadOnly - Checks whether is read only or not).
    5. Add, Clear, Remove - These methods are only valid if collection is not read only.
    Good thing about ICollection<T> interface is that it implements the IEnumerable<T> interface by virtue of which it can be traversed via a foreach loop.
    The declaration of the ICollection<T> interface is as follows
    Code:
    public interface ICollection<T> : IEnumerable<T>, IEnumerable
    {
        int Count { get; }
        bool Contains(T item);
        void CopyTo(T[] array, int ArrayIndex);
        bool IsReadOnly { get; }
    
        void Add(T item);
        void Clear();
        bool Remove(T item);
    }
    
    The non-generic version i.e ICollection is almost similar to the generic counterpart but it doesn’t provide functionalities to alter the collection. In addition to that ICollection interface also contains some methods that are used for synchronization purposes but with the advent of generic, thread safety capabilities were eliminated from generics and implemented separately. The declaration for non-generic ICollection interface is as follows.
    Code:
    public interface ICollection<T> : IEnumerable
    {
        int Count { get; }
        bool IsSynchronized { get; }
        object SynchRoot { get; }
        void CopyTo(Array array,int index);
    }
    
    Now, with the help of a working example, I will explain, how you can use ICollection<T> interface in your application.

    Example1
    Code:
    using System;
    using System.IO;
    using System.Collections;
    using System.Collections.Generic;
    using System.Text;
    using System.Numerics;
    
    namespace CSharpTutorial
    {
        class Program
        {
            public static void Main()
            {
                Employee obj1 = new Employee("Lara",10);
                Employee obj2 = new Employee("Mike", 15);
                Employee obj3 = new Employee("John", 20);
    
                HashSet<Employee> EmployeeCollection = new HashSet<Employee> { };
    
                EmployeeCollection.Add(obj1);
                EmployeeCollection.Add(obj2);
                EmployeeCollection.Add(obj3);
    
                Console.WriteLine("Number of Elements in collection: "+EmployeeCollection.Count);
    
                IEnumerator<Employee> numer = EmployeeCollection.GetEnumerator();
                Console.WriteLine("\nName    Age");
                Console.WriteLine("***********");
                while (numer.MoveNext())
                {
                    Employee emp = (Employee)numer.Current;
                    Console.WriteLine(emp.Name+"    "+emp.Age);      
                }
                Console.ReadLine();
            }
    
            public class Employee
            {
                string name;
                int age;
    
                public string Name
                {
                    get { return name; }
                
                }
    
                public int Age
                {
                    get { return age; }
                }
    
               public Employee(string name, int age)
                {
                    this.name = name;
                    this.age = age;
                }
            }
        }
    }
    
    In Example1, first jump to the Employee class. In the Employee class, we have two members: ‘name’ of type string and ‘age’ of type int. We have declared two properties Name and Age, which will return name and int member variables, respectively. We have a constructor in this method which is used to initialize name and int variable and since this is the only constructor, therefore we can initialize these values only via this constructor.
    Now come to the main method, here we created three objects of Employee class and named them obj1, obj2 and obj3. We initialized these objects with some values.
    You must be wondering about the next line i.e
    Code:
    HashSet<Employee> EmployeeCollection = new HashSet<Employee> { };
    
    In order to explain how collections implement ICollection<T> interface, we have used HashSet<T> collection. This collection implements both the ICollection<T> and ICollection interface. So, the HashSet<T> collection will contain all the methods of the ICollection <T> interface.

    In the next line, we have added the objects obj1, obj2 and obj3 into the EmployeeCollection using the Add method by virtue of HashSet<T>implementing the ICollection<T> interface. Next we have called another property of the ICollection<T> interface i.e Count to display the total number of elements in the EmployeeCollection, which would be three.

    Now next lines of code are extremely interesting, we have called GetEnumerator() method on the EmployeeCollection collection. Because this collection implements the ICollection<T> interface, which inherits from the IEnumerable<T> interface, we can call the GetEnumerator method to get the enumerator and store it in IEnumerable<T> interface’s object. We have named this object ‘numer’.

    We have then called MoveNext() method inside a while loop so that while loop keeps executing until all the elements in the collection have been traversed. Inside the loop we called Current property of the ‘numer’ object to fetch the current object and then we stored it inside an Employee object ‘emp’. From this ‘emp’ object, we displayed the Name and Age properties of the object. You can see that how we can use ICollection<T> interface to count the elements in a collection and add elements to them as well. You can also call other methods of the ICollection <T> interface on the EmployeeCollection. For example if you want to check that whether the collection contains ‘obj1’ or not you can do something like

    EmployeeCollection.Contains(obj1) // Will return true because collection contains obj1

    Similarly, you can try other ICollection<T> interface’s methods to better understand the concept. In Example1, EmployeeCollection was not read only. If you call add, clear or remove methods on a read only collection, an exception named NotSuppertedException would be thrown.

    IList<T> and IList Interfaces



    As aforementioned, the IList<T> along with its non-generic counterpart IList provides maximum functionalities over a collection. These interfaces allow to read and write element based on the index or position and it also allows us to insert and remove elements via index. These capabilities are not available in the ICollection<T> and IEnumerable<T> interfaces. An important thing to note here is that IList <T> interface inherits ICollection<T> and IEnumerable<T> interfaces, therefore it contains all the functionalities of those interfaces.

    Before dwelling into further details regarding the usage and implementation of IList<T> interface, let us first have a look at its declaration.
    Code:
    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);        
    }
    
    Here the IndexOf() method is used to get the index of the item that is passed to it. The Insert () method is used to insert an item on the specified index and finally the RemoveAt() method.

    Now let us come towards the declaration of IList interface. This interface has more methods. Since it only implements the non-generic ICollection and IEnumerable interfaces it has more functionalities of its own. The declaration of IList is as follows.
    Code:
    public interface IList : ICollection, IEnumerable
    {
        object this[int index] { get; set; }
        bool IsFixedSize { get; }
        bool IsReadOnly { get; }
    
        void Clear();
        int Add(object value);
    
        bool Contains(object value);
        int IndexOf(object value);
        void Insert(int index, T item);
        void Remove(object value);
        void RemoveAt(int index);        
    }
    
    Now we have seen the declaration and functions provided by the IList<T> and IList interface. Let us implement these interfaces in working example and see how they behave.

    Example2

    Code:
    using System;
    using System.IO;
    using System.Collections;
    using System.Collections.Generic;
    using System.Text;
    using System.Numerics;
    
    namespace CSharpTutorial
    {
        class Program
        {
            public static void Main()
            {
                Employee obj1 = new Employee("Lara",10);
                Employee obj2 = new Employee("Mike", 15);
                Employee obj3 = new Employee("John", 20);
    
                IList <Employee> EmployeeCollection = new List<Employee> { };
                
                EmployeeCollection.Add(obj1);
                EmployeeCollection.Add(obj2);
                EmployeeCollection.Add(obj3);
    
                Console.WriteLine("Number of Elements in collection: "+ EmployeeCollection.Count);
    
                Console.WriteLine(EmployeeCollection.IndexOf(obj1)); // Index Starts from 0
                Console.WriteLine(EmployeeCollection.IndexOf(obj2));
                Console.WriteLine(EmployeeCollection.IndexOf(obj3));
               
                EmployeeCollection.RemoveAt(1); // Removing Object at 1st index
                Employee obj4 = new Employee("jess", 21);
    
                EmployeeCollection.Insert(1, obj4); // Adding Object at 1st indes
    
                Console.WriteLine(EmployeeCollection.Contains(obj2));
    
                IEnumerator<Employee> numer = EmployeeCollection.GetEnumerator();
    
                Console.WriteLine("\nName    Age");
                Console.WriteLine("***********");
                while (numer.MoveNext())
                {
                    Employee emp = (Employee)numer.Current;
                    Console.WriteLine(emp.Name+"    "+emp.Age);
                    
                }
                Console.ReadLine();
            }
    
            public class Employee
            {
                string name;
                int age;
    
                public string Name
                {
                    get { return name; }
                
                }
    
                public int Age
                {
                    get { return age; }
                }
    
               public Employee(string name, int age)
                {
                    this.name = name;
                    this.age = age;
                }
            }
        }
    }
    
    Have a look at the code in Example2. At the end of the code snippet, we have the same Employee class which has two members: string ‘name’ and int ‘age’ and they are accessed via two properties, Name and Age respectively. And a constructor is used to initialize these members.

    Now, come inside the main method. We have initialized three objects of Employee class and we have named these objects obj1, obj2 and obj3. Then we simply called the ‘Count’ property in order to display the number of elements in the EmployeeCollection collection.

    The next lines are what differentiate this example from the Example2. Consider this line of code
    Code:
    IList <Employee> EmployeeCollection = new List<Employee> { };
    
    Here we have stored an instance of List<T> collection in IList<T> interface’s object. Basically, the List<T> is a generic collection located in System.Collections.Generic namespace and this collection implements the IList<T> interface. In order the explain the IList<T> interface we have used the List<T> collection, you can use any collection that implements the IList<T> interface or you can also write your own custom collection that implements the IList<T> interface.

    In the next three lines, we have called the IndexOf() method on the EmployeeCollection instance and have passed the three objects that we added previously i.e obj1, obj2 and obj3. We then call the RemoveAt() method on the Employee collection and passed it the index 1. An important thing to note here is that, the collection indexes start from ‘0’ which means that the first element would be on 0th index and the 2nd element would be stored in the first index. So when we call the RemoveAt(1) on EmployeeCollection, the obj2 would be removed because it is stored at the first index.

    After that, we have initialized a new Employee object obj4 and have inserted it into collection at the first index using the method Insert(1, obj4). Now we have three objects, obj1 at 0th index, obj4 at 1st index and obj3 at second index.

    Next, In order to check that obj2 has really been removed from the first index, we have called the Contains() method and passed it the obj2. It should return false. After that we have got the enumerator of the EmployeeCollection class by calling GetEnumerator in this collection and then have traversed each object using the MoveNext() method and ‘Current’ property of IEnumerator<T> interface which is implemented by IList<T> interface. The output of the code is as follows.

    [​IMG]

    In the output, first of all the number of element in the collection are displayed which are three i.e obj1, obj2 and obj3 at the moment. Next we have displayed their indexes which are 0, 1 and 2 respectively. And then we have displayed their names and ages. Note that the name and age of the second object (Object at first index) is ‘jess’ and ‘21’ respectively. This is due to the reason that we had removed the obj2 from first index and had inserted obj4 at that index, therefore the values of obj4 are being shown in the second row.

    The ICollection<T> and IList<T> interfaces, along with their generic counterparts are extremely useful, particularly in cases where you more advanced capabilities as compared to IEnumerable<T> and IEnumerable. In this article we have explained the detailed declaration and implementation of both the aforementioned interfaces. If you have any further question, feel free to comment.
     
    Last edited: Jan 21, 2017

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice