A step into the C# Generics Collection
Generics is a type parameter which allows you to specify an arbitrary type T to a method at compile time having no concrete type specification in the method or class declaration.
Introduction
One of the key concepts introduced in c# 2.0 was generic. A lot of articles has been written on generics but somehow still many developers don’t understand the basic concept of generics. It is very imperative to understand and include generics in the application where it’s required. Keeping this in mind, I decided to write some basic concept pertaining to generic.
Generic can be explained in a very simple way that it is a type parameter which allows you to specify an arbitrary type T to a method at compile time having no concrete type specification in the method or class declaration.
public class GenericClass { Public void SomeBehaviour<T> (T input) { } } |
A generic collection is strongly typed (type safe), meaning that you can only put one type of object into it. This will eliminate type mismatches at runtime. The main advantage of generic is code reusability, type-safe as well as performance.
Generics Collection
We have multiple types of a generics collection like a list, Dictionary, Sorted List, Queue. Here we are using List as a generic type collection. It means we can define the list of any type (but one type at a time). For example
List<int> myIntsList=new List<int> (); // this will create a list of integer at runtime. List<string> myStringList=new List<string> (); // this will create a list of string at run time. List<object> myObjectList=new List<object> (); // this will create a list of objects at run time. |
Generic vs Non-Generic
The non-generic collection like ArrayList, HashTable, SortedList are also used to hold/ persist records but it has some different behavior as compared to the generic collection. The non-generic collection will hold any type of data irrespective of the data type. For example
ArrayList arr = new ArrayList(); arr.Add("a"); arr.Add(4); arr.Add(new c1()); arr.Add(new object()); |
In a normal situation, array list looks good as compared to the list because we can add any type of data in it but it has huge performance issues. Every time when you will fetch record you have no idea of what type of data (int, string, float) will be returned. It may generate an exception at runtime because of wrong casting. To avoid casting errors, we have to cast the data in the appropriate type before it’s used, which is called boxing. This will hugely impact on performance. Let’s consider the above arryList.
string varstr = arr[0].ToString(); int varint = Convert.ToInt32(arr[1]); c1 objC1 = arr[2] as c1; object obj = arr[3]; |
Let’s understand generic with another example.
We are creating a method which will reverse the array of any type.
public T[] Reverse<T>(T[] array) { var result = new T[array.Length]; int j=0; for(int i=array.Length; i>= 0; i--) { result[j] = array[i]; j++; } return result; } |
In the above method, we can understand that a single Reverse method will reverse the array irrespective of any data type.
int[] arrayInt = Reverse(arrayOfInt); string[] arrayString = Reverse(ListOfString.ToArray()); |
Real world Example:
Let’s consider in an enterprise application, we are required to make CRUD (create, read, update, delete) operation of all objects (tables)
We can easily achieve these task by create a class with Generic Methods.
First, create a generic class.
public class GenericRepository<TEntity> where TEntity : class |
Then add Database context and DBSet in the generic class.
internal KSC_RECONCILEEntities context; internal DbSet<TEntity> dbSet; public GenericRepository() { this.context = new KSC_RECONCILEEntities(); this.dbSet = context.Set<TEntity>(); } |
Now creating insert method into the class which will add the entity in the database set and call context’s save property to save in the database.
public virtual void Insert(TEntity entity) { dbSet.Add(entity); } |
So instead of writing separate insertion method for each entity, we have used generics. Finally, call the method with various entities.
GenericRepository<Student> studentObj = new GenericRepository<Student>(); obj.Insert(studentObj); GenericRepository<Course> courseObj = new GenericRepository<Course>(); obj.Insert(courseObj); |
The same goes for the other methods like an update, delete and get.
public virtual void Delete(object id) { TEntity entityToDelete = dbSet.Find(id); Delete(entityToDelete); } public virtual void Delete(TEntity entityToDelete) { if (context.Entry(entityToDelete).State == EntityState.Detached) { dbSet.Attach(entityToDelete); } dbSet.Remove(entityToDelete); } public virtual void Update(TEntity entityToUpdate) { dbSet.Attach(entityToUpdate); context.Entry(entityToUpdate).State = EntityState.Modified; } public virtual TEntity GetByID(object id) { return dbSet.Find(id); } |
Conclusion
Generics is a very significant feature in development which reduces code repetition, performance enhancement, type safety and code quality in a very decent and understandable syntax.