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#:
using System;
namespace HP.Mahesh.GenericsExample.Methods.CSharp
{
class GenericsSample
{
static void Main(string[] args)
{
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 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();
}
public static void
Swap<GENERICTYPE>(ref GenericType firstParameter,
ref GenericType secondParameter)
{
GenericType temp;
temp = firstParameter;
firstParameter = secondParameter;
secondParameter = temp;
}
}
}
In VB.NET:
Namespace HP.Mahesh.GenericsExample.Methods.VBNet
Public Class GenericsSample
Shared Sub Main()
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)
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
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 Class
End Namespace
The output for both the code snippets result in:
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#:
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:
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:
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#:
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());
}
}
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];
}
}
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#:
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();
}
}
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];
}
}
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 );
}
}
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());
}
}
}
In VB.NET:
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
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
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
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 Namespace
The output for both the code snippets:
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.
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.