What are generics? An easy name for parametric polymorphism that refer to classes and methods that work homogeneously on values of different types. The code of this type of data structure remains the same; though, the data type of the parameters can change with each use. Basically, a generic is a code template that can be used over and over with different data types.
Why use them?
1. They're safer (more bugs are caught at compile time)
2. They're more expressive (more invariants are expressed in type signatures)
3. They provide more clarity (fewer explicit conversions between data types)
4. They're more efficient (no need for run-time type checks)
5. You'll never have to write a strongly typed collection again!
Generics allow you to specify the type to work with.
Topic Areas Covered in this tutorial How To Use Generics Sorting With Generics Sorting Directionally With GenericsNOTE: Throughout this page, you will find the use of the letter "T" to represent a data type.
In the 1.1 Framework example below, whenever you worked with a collection, you had to type check what you were adding becauseyou wold not get a compile time error.
C# 1.1 Framework |
VB.Net |
ArrayList items = new ArrayList(); items.Add("my string"); items.Add(123); |
Dim items As New ArrayList items.Add("my string") items.Add(123) |
But, now if you use generics, you will get a compile time error if the type is incorrect. This ensures that incompatible types are not added toNOTE: VB.Net may not throw the error while compiling this, but will when executing.
C# 2.0 Framework |
VB.Net |
List<string> items = new List<string>(); items.Add("my string"); // No Compile Error items.Add(123); // Compile Error |
Dim items As New List(Of Integer) items.Add("my string") items.Add(123) |
To show, how Generics can be used, let's start by creating a "User" class.
1. Create a solution.
2. Add a new "Class Library" Project to the Solution called "Generics".
3. In the class properties, set the Default namespace to "Generics".
4. Remove the Class1.cs file
5. Add a new class called "User".
6. Add the following methods below.
C# |
VB.Net |
using System; using System.Collections.Generic; using System.Text;
namespace Generics { public class User { string userName = string.Empty;
public User(string userName) { this.userName = userName; }
public string UserName { get { return userName; } set { userName = value; } } } } |
Imports System Imports System.Collections.Generic Imports System.Text
Public Class User
Dim _userName As String
Public Property UserName() As String Get Return _userName End Get Set(ByVal value As String) _userName = value End Set
Public Sub New(ByVal UserName As String) _userName = UserName End Sub |
Next, I'll show you how to use generics based on List
(Remember 'T' means DataType)
1. Right click on your solution and add a "New" project
2. Add a new "Console Application" Project to the Solution called "ConsoleApp".
3. In the class properties, set the Default namespace to "GenericTest".
4. Remove the Class1.cs file
5. Right click on References and add new reference (Navigate to Projects tab and select the Generics Project)
6. Add a new class called "User".
7. Add the following methods below.
NOTE: For VB.Net, you will have to right click on the Console project and add a reference to the User Project.
C# |
VB.Net |
using System; using System.Collections.Generic; using System.Text; using Generics; //This is the Project above. namespace GenericTest { class program { static void Main(string[] args) { List<User> users = new List<User>(); users.Add(new User("Dave is cool")); users.Add(new User("No, Dave is awesome")); foreach (User u in users) Console.WriteLine(u.UserName); Console.ReadLine(); } } } |
Imports Generic
Module User
Sub Main()
Dim users As New List(Of User)
users.Add(New User("Dave is cool")) users.Add(New User("Yes, he's awesome"))
For Each u As User In users Console.WriteLine(u.UserName) Next
Console.ReadLine()
End Sub
End Module |
This produces the results below
Dave is Cool No, Dave is awesome |
Sorting With GenericsNext, I'll build a sorter that will allow you to sort the generic based on one of the properties within the class.
1. Let's add another property to our class so we can choose what we want to sort by.
2. Open up our "User" class in our Class Library and add a property called "Address"
3. Below the userName property, type the following... font>
C# |
VB.Net |
string address = string.Empty; |
Dim _address As String |
4. Next right click on the word "Address" and select "Refactor | Encapsulate Field..."
5. This will refactor the private variable and build a property for you. (In VB.Net you will have to do this manually, unless you have a refactoring toolkit).
6. It should look like the code below:
C# |
VB.Net |
public string Address { get { return address; } set { address = value; } } |
Public Property Address() As String Get Return _address End Get Set (ByVal value As String) _address = value End Set End Property |
7. Be sure to add any new Properties to the constructor, E.g....
C# |
VB.Net |
public User(string UserName, string Address) { this.userName = UserName; this.address = Address; } |
Public Sub New(ByVal Username As String, _ ByVal Address As String) _userName = UserName _address = Address End Sub |
8. To allow the class to be sorted based on a property, we need to first change the class signature by implementing IComparable, as follows...
C# |
VB.Net |
public class User : IComparable<User> { ... } |
Public Class User Implements IComparable(Of User) |
9. Now, because we implemented IComparable, we must add a CompareTo method, as follows... NOTE: We could have picked any numeric or string type property to be the defualt comparer for the class.
C# |
VB.Net |
public int CompareTo(User other) { return userName.CompareTo(other.userName); } |
Public Function CompareTo(ByVal other As User) _ As Integer Implements _ IComparable(Of Generic.User).CompareTo
Return Me.UserName.CompareTo(other.UserName) End Function |
10. To test the sort capability of your Generic class, copy the following code to your Console application and run.
C# |
VB.Net |
using System; using System.Collections.Generic; using System.Text; using Generics; //This is the Project above. namespace GenericTest { class program { static void Main(string[] args) { List<User> users = new List<User>(); users.Add(new User("A", "1")); users.Add(new User("C", "2")); users.Add(new User("B", "3")); foreach (User u in users) Console.Write("{0}\t{1}\n", u.UserName, u.Address); Console.ReadLine(); users.Sort();
foreach (User u in users) Console.Write("{0}\t{1}\n", u.UserName, u.Address); Console.ReadLine(); } } } |
Imports Generic
Module User
Sub Main()
Dim users As New List(Of User)
users.Add(New User("A", "1")) users.Add(New User("C", "2")) users.Add(New User("B", "3"))
For Each u As User In users Console.WriteLine(u.UserName & " " & u.Address) Next
Console.WriteLine() users.Sort()
For Each u As User In users Console.WriteLine(u.UserName & " " & u.Address) Next
Console.ReadLine()
End Sub End Module |
This produces the results below
Sorting Directionally With Generics Next, I'll show you how to Sort ascending or decending on any field within the class.
1. The first thing to add is an Enumeration within our Namespace for the direction
C# |
VB.Net |
public enum SortDirection { ASC, DESC } |
Public Enum SortDirection ASC DESC End Enum |
2. Now we will create a new Comparer class for each property that we want to sort on. E.g....
a. First, we create a private direction property with a default direction of Ascending.
b. Then, the property for the direction is created.
c. Followed by the Compare method to do the sorting.
C# |
VB.Net |
public class UserNameCompare : IComparer<User> { protected SortDirection direction = SortDirection.ASC;
public SortDirection Direction { get { return direction; } set { direction = value; } }
public int Compare(User x, User y) { if (Direction == SortDirection.ASC) return x.UserName.CompareTo(y.UserName); else return x.UserName.CompareTo(y.UserName) * -1; } } |
Public Class UserNameCompare Implements IComparer(Of User)
Protected _direction As SortDirection = SortDirection.ASC
Public Property Direction() As SortDirection Get Return _direction End Get Set(ByVal value As SortDirection) _direction = value End Set End Property
Public Function Compare(ByVal x As Generic.User, ByVal y As Generic.User) _ As Integer Implements _ System.Collections.Generic.IComparer(Of Generic.User).Compare
If _direction = SortDirection.ASC Then Return x.UserName.CompareTo(y.UserName) Else Return x.UserName.CompareTo(y.UserName) * -1 End If End Function End Class |
3. Next we'll build one for the Address property to show that it works
C# |
VB.Net |
public class AddressCompare : IComparer<User> { protected SortDirection direction = SortDirection.ASC;
public SortDirection Direction { get { return direction; } set { direction = value; } }
public int Compare(User x, User y) { if (Direction == SortDirection.ASC) return x.Address.CompareTo(y.Address); else return x.Address.CompareTo(y.Address) * -1; } } |
Public Class AddressCompare Implements IComparer(Of User)
Protected _direction As SortDirection = SortDirection.ASC
Public Property Direction() As SortDirection Get Return _direction End Get Set(ByVal value As SortDirection) _direction = value End Set End Property
Public Function Compare(ByVal x As Generic.User, ByVal y As Generic.User) _ As Integer Implements _ System.Collections.Generic.IComparer(Of Generic.User).Compare
If _direction = SortDirection.ASC Then Return x.Address.CompareTo(y.Address) Else Return x.Address.CompareTo(y.Address) * -1 End If End Function End Class |
4. To show how we can use directional sorting in our application, paste the following code in your Console application and give it a try.
C# |
VB.Net |
using System; using System.Collections.Generic; using System.Text; using Generics; //This is the Project above. namespace GenericTest { class program { static void Main(string[] args) { List<User> users = new List<User>(); users.Add(new User("A", "1")); users.Add(new User("C", "2")); users.Add(new User("B", "3")); foreach (User u in users) Console.Write("{0}\t{1}\n", u.UserName, u.Address);
Console.WriteLine("\n");
UserNameCompare myNameComp = new UserNameCompare(); myNameComp.Direction = SortDirection.DESC; users.Sort(myNameComp);
foreach (User u in users) Console.Write("{0}\t{1}\n", u.UserName, u.Address); Console.WriteLine("\n");
AddressCompare myAddComp = new AddressCompare(); myAddComp.Direction = SortDirection.DESC; users.Sort(myAddComp);
foreach (User u in users) Console.Write("{0}\t{1}\n", u.UserName, u.Address); Console.ReadLine(); } } } |
Imports Generic
Module User
Sub Main()
Dim users As New List(Of User)
users.Add(New User("A", "1")) users.Add(New User("C", "2")) users.Add(New User("B", "3"))
For Each u As User In users Console.WriteLine(u.UserName & " " & u.Address) Next
Console.WriteLine() Dim myNameComp As UserNameCompare = _ New userNameCompare() myNameComp.Direction = SortDirection.DESC users.Sort(myNameComp)
For Each u As User In users Console.WriteLine(u.UserName & " " & u.Address) Next Console.WriteLine() Dim myAddComp As AddressCompare = _ New AddressCompare() myAddComp.Direction = SortDirection.DESC users.Sort(myAddComp)
For Each u As User In users Console.WriteLine(u.UserName & " " & u.Address) Next
Console.ReadLine()
End Sub End Module |
This produces the results below
A 1 C 2 B 3
C 2 B 3 A 1
B 3 C 2 A 1 |