Translate

C# Generics

C# Generics are really useful when we want to reuse a class for different types and with constraints on what those types would be without rewriting those classes.

C# allows at least 5 different kinds of Generics usage as related to the kind of constraints that we can use plus the most basic implementations such a simple Generic list or Generic Dictionary.

1. Simple Generic List

    Book Class
public class Book
    {
        public string Name { get; set; }
        public string Title { get; set; }
 
    }

    GenericList Class
public class GenericsList<T>
    {
 
        public void Add(T value)
        {
            
        }
        
    }
      
 Usage - Note that we can add items of type integer and of type Book
using System.Collections.Generic;
 
namespace Generics
{
    class Program
    {
        
       static void Main(string[] args)
        {
            var number = new GenericsList<int>();
            number.Add(10);
 
            var book = new GenericsList<Book>();
            book.Add(new Book { Name = "Bible", Title = "Genesis"}); 
                      
        }
    }
}

If there was no Generics we would have to write two separate classes - one to handle integer and the other to handle Object Type Book.

2. Simple Generic Dictionary

    Book Class
public class Book
    {
        public string Name { get; set; }
        public string Title { get; set; }
 
    }

    GenericDictionary Class
public class GenericDictionary<TKey, TValue>
    {
        public void Add(TKey key, TValue value)
        {
            
        }
        
    }

      Usage - Note that we can add items of type string and of type Book
using System.Collections.Generic;
 
namespace Generics
{
    class Program
    {
        
       static void Main(string[] args)
        {
            var dict = new GenericDictionary<string, Book>();
            dict.Add("first",new Book {Name = "Bible", Title = "Exodus"});
           
        }
    }
}

3. Generic Class with constraint on a reference type (class/object)

Note how we restrict the type that the class can use. In this case the class is allowed to use only CommonStock Type.

public class CommonStock
    {
        public string Title { get; set; }
        public float Price { get; set; }
    }

//example for Generic Class with constraint on class/object type
    public class MarketWatch<TStock> where TStock : CommonStock
    {
        public float CalculatePrice(TStock stock)
        {
            ....
            return (stock.Price);
        }
    }
    
4. Generic Class with constraint on an interface

Lets say we want to compare two "things" and not bother about the types and want to use a single class/method to achieve this purpose. Below is how we achieve this via Generics -
using System;
 
namespace Generics
{
    public class Utilities<T> where T : IComparable
        public T Max(T a, T b)
        {
            return a.CompareTo(b) > 0 ? a : b; 
        }
    }
}

Note how we define the Generic Type T and put constraint on it so it implements Icomparable interface that will provide us with the necessary object comparison methods.

5. Generic Class with constraint on Type which is an object that has default constructor

Below is an example where we want to instantiate and instance of the type T.

using System;
 
namespace Generics
{
    public class Utilities<T> where T : new()
    {
        public void testNew(T value)
        {
            var obj = new T();
        }
    }
}

6. Generic Class with constraint on a value type (struct)

Lets now see a last example where we can use Generic Type where it needs to be of value type, say struct. A good real life example is the implementation of nullable type. We know that C# does not allow nullable for value types eg: int cannot be null. But we can implement value type as nullable using Generics as shown below - 

namespace Generics
{
    public class Nullable<T> where T : struct
    {
        private object _value;
 
        public Nullable()
        {
 
        }
        public Nullable(T _value)
        {
            this._value = _value;
        }
 
        public bool HasValue
        {
            get
            {
                return _value != null;
            }
        }
 
        public T GetValueOrDefault()
        {
            if (HasValue)
                return (T) _value;
            else
            {
                return default(T);
            }
        }
    }
}

Here we a private variable _value of type object and provide methods to access its value. HasValue method returns null if _value does not have a value. GetValueOrDefault returns the actual value if _value is not null, else it returns the default value of the object Type, which is 0 in case of integer. This is run through in below main program for verification -

using System;
using System.Collections.Generic;

 
namespace Generics
{
    class Program
    {
        
       static void Main(string[] args)
        {             
           var nullable = new Nullable<int>(10);
 
           //returns 10 on console
           Console.WriteLine(nullable.GetValueOrDefault());
 
           //returns 0 on console
           var number2 = new Nullable<int>();
           Console.WriteLine(number2.GetValueOrDefault()); 
           
        }
    }
}