Click here to Skip to main content
15,867,686 members
Articles / Programming Languages / Visual Basic
Article

Generics and Custom Collection Classes in .NET 2.0

Rate me:
Please Sign up or sign in to vote.
3.50/5 (19 votes)
30 Sep 20053 min read 130.5K   697   37   8
An article on how Generics in .NET 2.0 Framework can help writing custom collection classes easily.

Introduction

Creating custom collection classes in the data layer has been a practice that was carried on from my VB 6.0 days onwards. Writing a collection class was more or less copy & paste always. It used to be really pain-taking doing this. And many a times this also made the team miss the correct class definitions in the collection class, which were mostly identified only during unit testing.

Nothing changed with the coming of .NET either. The same practice continued. But with the 2.0 framework things are changing, keeping me and the team happier. In this article let me share the joy I experienced when I learned the use of Generics.

Generics is similar to Templates as in C++. The underlying concept of Templates in C++ and Generics in .NET is the same - define a method or class, specifying the data type as a replaceable element.

A quick look at Generics

Generics can be used in the method definitions as well as in classes. Let us see an example of the implementation. First let us see the generics implementation of a single method. We will take up the classic example of the Swap method.

In C#:

C#
using System;
namespace HP.Mahesh.GenericsExample.Methods.CSharp
{
    class GenericsSample
    {
        static void Main(string[] args)
        {
            //Integer parameters against the Swap method
            Int32 a = 10, b = 20;
            System.Console.WriteLine("Value of a, b are {0} and {1}", a, b);
            Swap<INT32>(ref a, ref b);
            System.Console.WriteLine("Value of a, b " + 
               "after swapping are {0} and {1}", a, b);

            //String parameters against the Swap method
            String a1 = "First", a2 = "Second";
            System.Console.WriteLine("Value of strings a1," + 
                              " a2 are {0} and {1}", a1, a2);
            Swap<STRING>(ref a1, ref a2);
            System.Console.WriteLine("Value of strings a1," + 
                " a2 after swapping are {0} and {1}", a1, a2);
            
            System.Console.ReadLine();
        }//end of the Main Method

        public static void 
               Swap<GENERICTYPE>(ref GenericType firstParameter, 
                                     ref GenericType secondParameter)
        {
            GenericType temp;
            temp = firstParameter;
            firstParameter = secondParameter;
            secondParameter = temp;
        }//end of the Swap method
    }
}

In VB.NET:

VB
Namespace HP.Mahesh.GenericsExample.Methods.VBNet

    Public Class GenericsSample
        Shared Sub Main()
            'Integer Parameters against the Swap method
            Dim a As Integer = 10, b As Integer = 20
            System.Console.WriteLine("Value of a, b are {0} and {1}", a, b)
            Swap(Of Integer)(a, b)
            System.Console.WriteLine("Value of a, b" & _ 
                 " after swapping are {0} and {1}", a, b)

            'String parameters against the Swap method
            Dim a1 As String = "First", a2 As String = "Second"
            System.Console.WriteLine("Value of strings" & _ 
                       " a1, a2 are {0} and {1}", a1, a2)
            Swap(Of String)(a1, a2)
            System.Console.WriteLine("Value of strings a1, " & _ 
                     "a2 after swapping are {0} and {1}", a1, a2)

            System.Console.ReadLine()
        End Sub 'End of Main Method
        Public Shared Sub Swap(Of GenericType)_
               (ByRef firstParameter As GenericType, _
                ByRef secondParameter As GenericType)
            Dim temp As GenericType
            temp = firstParameter
            firstParameter = secondParameter
            secondParameter = temp
        End Sub 'End of Swap Method
    End Class
End Namespace

The output for both the code snippets result in:

Image 1

We can see from the code snippet that implementation of Generics in VB or C# differs only with the syntactical usage. The basic implementation pattern remains the same.

Generics can also be applied on classes that encapsulate operations that are not specific for a particular data type. The most common use of generic classes is with collections like linked lists, stacks, queues, trees etc. This usage will allow a common approach for adding or removing elements from the collection irrespective of the data type of the elements.

Let us create a sample class to get a feel of this. We will take the example of the common stack class. The class will hold Push and Pop methods. While implementing, we can specify which data type the object can hold. In the example below, we implement stringStack to accept string elements and intStack to accept integer elements. The code snippet for the same:

In C#:

C#
using System;

namespace HP.Mahesh.GenericsExample.Classes.CSharp
{

    class MyStack<GENERICTYPE>
    {
        private GenericType[] items;
        private int count;
        public MyStack(Int32 Size)
        {
            items = new GenericType[Size];
            count = -1;
        }
        public void Push(GenericType item)
        {
            items[++count] = item;
        }
        public GenericType Pop()
        {
            return (items[count--]);
        }
    }
    class GenericsSample
    {
        static void Main(string[] args)
        {
            MyStack<STRING> stringStack = new MyStack<STRING>(10);
            stringStack.Push("TSG");
            stringStack.Push("IPG");
            stringStack.Push("ASDU");
            System.Console.WriteLine("String Stack in action");
            System.Console.WriteLine(stringStack.Pop().ToString());
            System.Console.WriteLine(stringStack.Pop().ToString());
            System.Console.WriteLine(stringStack.Pop().ToString());
            MyStack<INT32> intStack = new MyStack<INT32>(10);
            intStack.Push(10);
            intStack.Push(20);
            intStack.Push(30);
            System.Console.WriteLine("Integer Stack in action");
            System.Console.WriteLine(intStack.Pop().ToString());
            System.Console.WriteLine(intStack.Pop().ToString());
            System.Console.WriteLine(intStack.Pop().ToString());

            System.Console.ReadLine();
        }
    }    
}

In VB.NET:

VB
Namespace HP.Mahesh.GenericsExample.Classes.VBNet
    Class MyStack(Of GenericType)
        Private items() As GenericType
        Private count As Integer
        Sub New(ByVal Size As Integer)
            ReDim items(Size)
            count = -1
        End Sub
        Sub Push(ByVal item As GenericType)
            count += 1
            items(count) = item
        End Sub
        Function Pop() As GenericType
            Dim returnItem As GenericType
            returnItem = items(count)
            count -= 1
            Return (returnItem)
        End Function
    End Class
    Module modGenericSample
        Sub Main()
            Dim stringStack As New MyStack(Of String)(3)
            stringStack.Push("TSG")
            stringStack.Push("IPG")
            stringStack.Push("ASDU")
            System.Console.WriteLine("String Stack in action")
            System.Console.WriteLine(stringStack.Pop().ToString())
            System.Console.WriteLine(stringStack.Pop().ToString())
            System.Console.WriteLine(stringStack.Pop().ToString())

            Dim intStack As New MyStack(Of Integer)(3)
            intStack.Push(10)
            intStack.Push(20)
            intStack.Push(30)
            System.Console.WriteLine("Integer Stack in action")
            System.Console.WriteLine(intStack.Pop().ToString())
            System.Console.WriteLine(intStack.Pop().ToString())
            System.Console.WriteLine(intStack.Pop().ToString())
            System.Console.ReadLine()
        End Sub
    End Module
End Namespace

From the examples, again it is clear that the Generics implementation in C# and VB.NET differs only syntactically and also that Generics can be used in any type of class where it is necessary to use different data types in the implementation of these classes.

The output of both the code snippets will be:

Image 2

Writing Custom Collection Classes

In most of our design we write a class, a custom collection class and use this collection class in our Data Layer to hold the set of objects. Let us see how this is done in .NET using the CollectionBase.

In C#:

C#
using System;
using System.Collections;
namespace HP.Mahesh.GenericsExample.CollectionImplementations
{
    class Employee
    {
        private string strEmployeeName;
        private string strEmployeeID;
        private Int32 intEmployeeAge;
        public Employee(string EmployeeName, string EmployeeID, Int32 Age)
        {
            strEmployeeName = EmployeeName;
            strEmployeeID = EmployeeID;
            intEmployeeAge = Age;
        }
        public new string ToString()
        {
            return ("Name : " + strEmployeeName + "\n" +
                    "ID   : " + strEmployeeID + "\n" +
                    "Age  : " + intEmployeeAge.ToString());
        }
    } //end of Employee class

    class Employees :
                    CollectionBase
    {
        public void Add(Employee empObject)
        {
            InnerList.Add(empObject);
        }

        public void Remove(int index)
        {
            InnerList.RemoveAt(index);
        }
        public Employee Item(int index)
        {
            return (Employee)InnerList[index];
        }
        
    } //end of Employees class

    public class GenericsSample
    {
        public static void Main(string[] args)
        {
            Employees empCollection = new Employees();
            empCollection.Add(new Employee("Mahesh", "20049986", 28));
            empCollection.Add(new Employee("Aravind", "20059986", 32));
            foreach (Employee emp in empCollection)
            {
                System.Console.WriteLine(emp.ToString());
            }
            System.Console.ReadLine();
        }
    }
}

Here we have not taken the database interaction in the above example and also the complete Data Layer as we are only trying to find the possibilities to reduce our effort in creating custom collection classes. In case our design has 10 objects then we need to write 10 collection classes for each class. This is where we can use the strength of Generics in .NET to help us. Let us see how this can be done.

First we can write a generic collection class which can accept any type. The code snippet for the same:

In C#:

C#
using System;
using System.Collections;
using System.Collections.Generic;

namespace HP.Mahesh.GenericsExample.GenericCollections
{
    class GenericClassImplementation
    {
        public static void Main(string[] cmdLine)
        {
            GenericCollection<CUSTOMER> Customers = 
                 new GenericCollection<CUSTOMER>();
            GenericCollection<EMPLOYEE> Employess = 
                 new GenericCollection<EMPLOYEE>();

            Customers.Add(new Customer("GM", "SH000100"));
            Customers.Add(new Customer("P & G", "SH000110"));
            Customers.Add(new Customer("Solectron - Belgium", "SH000120"));

            Employess.Add(new Employee("Mahesh","20049986",28));
            Employess.Add(new Employee("Aravind","20059986",32));

            System.Console.WriteLine("Customers");
            System.Console.WriteLine("=========");
            foreach (Customer cust in Customers)
            {
                System.Console.WriteLine(cust.ToString());
            }

            System.Console.WriteLine("Employees");
            System.Console.WriteLine("=========");
            foreach (Employee emp in Employess)
            {
                System.Console.WriteLine(emp.ToString());                
            }

            System.Console.ReadLine();

        }

    }//end of GenericClassImplementation class

    class GenericCollection<GENERICTYPE>
        : CollectionBase
    {
        public void Add(GenericType GenericObject)
        {
            InnerList.Add(GenericObject);
        }

        public void Remove(int index)
        {
            InnerList.RemoveAt(index);
        }
        public GenericType Item(int index)
        {
            return (GenericType)InnerList[index];
        }

    }//end of GenericCollection<GENERICTYPE>

    class Customer
    {
        private string strCustomerName;
        private string strCustomerShipTo;
        
        public Customer(string CustomerName, string ShipTo)
        {
            strCustomerName = CustomerName;
            strCustomerShipTo = ShipTo;            
        }
        public new string ToString()
        {
            return ("Name     : " + strCustomerName + "\n" +
                    "Ship To : " + strCustomerShipTo );
        }

    } //end of Customer


    class Employee
    {
        private string strEmployeeName;
        private string strEmployeeID;
        private Int32 intEmployeeAge;
        public Employee(string EmployeeName, string EmployeeID, Int32 Age)
        {
            strEmployeeName = EmployeeName;
            strEmployeeID = EmployeeID;
            intEmployeeAge = Age;
        }
        public new string ToString()
        {
            return ("Name : " + strEmployeeName + "\n" +
                    "ID   : " + strEmployeeID + "\n" +
                    "Age  : " + intEmployeeAge.ToString());
        }

    } //end of Employee
}

In VB.NET:

VB
Imports System.Collections.Generic
Namespace HP.Mahesh.GenericsExample.VNet.GenericCollections
    Class GenericClassImplementation
        Shared Sub Main()
            Dim Customers As GenericCollection(Of Customer) = _
                           New GenericCollection(Of Customer)()
            Dim Employees As GenericCollection(Of Employee) = _
                           New GenericCollection(Of Employee)()
            Customers.Add(New Customer("GM", "SH000100"))
            Customers.Add(New Customer("P & G", "SH000110"))
            Customers.Add(New Customer("Solectron - Belgium", "SH000120"))
            Employees.Add(New Employee("Mahesh", "20049986", 28))
            Employees.Add(New Employee("Aravind", "20059986", 32))
            Dim cust As Customer
            Dim emp As Employee
            System.Console.WriteLine("Customers")
            System.Console.WriteLine("=========")
            For Each cust In Customers
                System.Console.WriteLine(cust.ToString())
            Next
            System.Console.WriteLine("Employees")
            System.Console.WriteLine("=========")
            For Each emp In Employees
                System.Console.WriteLine(emp.ToString())
            Next
            System.Console.ReadLine()
        End Sub
    End Class 'End of GenericClassImplementation class
    Class Customer
        Dim strCustomerName As String
        Dim strCustomerShipTo As String
        Sub New(ByVal CustomerName As String, ByVal ShipTo As String)
            strCustomerName = CustomerName
            strCustomerShipTo = ShipTo
        End Sub
        Public Overrides Function ToString() As String
            Return ("Name     : " + strCustomerName + vbCrLf + _
                    "Ship To : " + strCustomerShipTo)
        End Function
    End Class 'End of Customer Class
    Class Employee
        Dim strEmployeeName As String
        Dim strEmployeeID As String
        Dim intEmployeeAge As Integer
        Sub New(ByVal EmployeeName As String, ByVal EmployeeID _
                               As String, ByVal Age As Integer)
            strEmployeeName = EmployeeName
            strEmployeeID = EmployeeID
            intEmployeeAge = Age
        End Sub
        Public Overrides Function ToString() As String
            Return ("Name : " + strEmployeeName + vbCrLf + _
                        "ID   : " + strEmployeeID + vbCrLf + _
                        "Age  : " + intEmployeeAge.ToString())
        End Function
    End Class 'End of Employee Class
    Class GenericCollection(Of GenericType)
        Inherits Collections.CollectionBase
        Public Sub Add(ByVal GenericObject As GenericType)
            InnerList.Add(GenericObject)
        End Sub
        Public Sub Remove(ByVal index As Integer)
            InnerList.RemoveAt(index)
        End Sub
        Public Function Item(ByVal index As Integer) As GenericType
            Return CType(InnerList.Item(index), GenericType)
        End Function
    End Class 'End of GenericCollection class
End Namespace

The output for both the code snippets:

Image 3

From the above code snippet we can see that one collection class can help in deriving many custom collection classes. These custom classes can then be used in the data layer.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
United States United States
Mahesh Kumar V K works for Hewlett Packard, from Chennai as a Senior Software Engineer. He had been associated with the organization for around 3years. He's been programming since 1997 using MS Tools. He is a passionate supporter of all flavours of VB. He loves to share his programming experiences with others and is a regular contributor in various other websites.

Comments and Discussions

 
QuestionSorting? Pin
MichaelTint25-Sep-07 22:46
MichaelTint25-Sep-07 22:46 
AnswerRe: Sorting? Pin
Pratik.Patel4-Oct-07 23:51
Pratik.Patel4-Oct-07 23:51 
Generalgood Article Pin
thiru_thiru20-Aug-07 22:16
thiru_thiru20-Aug-07 22:16 
GeneralExcellent Pin
C Jones28-Mar-07 5:10
C Jones28-Mar-07 5:10 
Generalthis article contains mistakes Pin
astrouk26-Feb-07 12:43
astrouk26-Feb-07 12:43 
GeneralRe: this article contains mistakes Pin
Sameer Kamat28-Feb-07 9:36
Sameer Kamat28-Feb-07 9:36 
GeneralRe: this article contains mistakes Pin
dkil19729-Apr-08 6:18
dkil19729-Apr-08 6:18 
QuestionSorting the collections??? Pin
iwonder26-May-06 6:38
iwonder26-May-06 6:38 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.