top of page

C# Generics

Generic is a class which allows the user to define classes and methods with the placeholder. Generics were added to version 2.0 of the C# language. The basic idea behind using Generic is to allow type (Integer, String, … etc., and user-defined types) to be a parameter to methods, classes, and interfaces. A primary limitation of collections is the absence of effective type checking. This means that you can put any object in a collection because all classes in the C# programming language extend from the object base class. This compromises type safety and contradicts the basic definition of C# as a type-safe language. In addition, using collections involves a significant performance overhead in the form of implicit and explicit type casting that is required to add or retrieve objects from a collection.


Generic Methods

Generic method type parameter hides the type parameter of Generic classes. CLR will not issue any warnings or errors, when we use the same type parameter name with both generic method and generic class.


A method declared with the type parameters for its return type or parameters is called a generic method.

class DataStore<T> {     
    private T[] _data = new T[10];          
    public void AddOrUpdate(int index, T item)     
    {         
        if(index >= 0 && index < 10)             
            _data[index] = item;     
    }      
    public T GetData(int index)     
    {         
        if(index >= 0 && index < 10)             
            return _data[index];         
        else
            return default(T);     
    } 
} 

Above, the AddorUpdate() and the GetData() methods are generic methods. The actual data type of the item parameter will be specified at the time of instantiating the DataStore<T> class, as shown below.


Example: Generics Method

using System;
					
public class Program
{
	public static void Main()
	{
		DataStore<string> cities = new DataStore<string>();
		cities.AddOrUpdate(0, "Mumbai");
		cities.AddOrUpdate(1, "Chicago");
		cities.AddOrUpdate(2, "London");

		Console.WriteLine(cities.GetData(100));

		DataStore<int> empIds = new DataStore<int>();
		empIds.AddOrUpdate(0, 50);
		empIds.AddOrUpdate(1, 65);
		empIds.AddOrUpdate(2, 89);
		
		Console.WriteLine(empIds.GetData(0));
	}
}

class DataStore<T>
{
    private T[] _data = new T[10];
    
    public void AddOrUpdate(int index, T item)
    {
        if(index >= 0 && index < 10)
            _data[index] = item;
    }

	public T GetData(int index){
		if(index >= 0 && index < 10)
			return _data[index];
		else
			return default(T);
	}
}

Output:


The generic parameter type can be used with multiple parameters with or without non-generic parameters and return type. The followings are valid generic method overloading.

public void AddOrUpdate(int index, T data) { } 
public void AddOrUpdate(T data1, T data2) { } 
public void AddOrUpdate<U>(T data1, U data2) { } 
public void AddOrUpdate(T data) { }


Generic Class

Generics in C# is its most powerful feature. It allows you to define the type-safe data structures. This out-turn in a remarkable performance boost and high-grade code, because it helps to reuse data processing algorithms without replicating type-specific code. Generics are similar to templates in C++ but are different in implementation and capabilities. Generics introduces the concept of type parameters, because of which it is possible to create methods and classes that defers the framing of data type until the class or method is declared and is instantiated by client code. Generic types perform better than normal system types because they reduce the need for boxing, unboxing, and type casting the variables or objects.


Generic classes are defined using a type parameter in an angle brackets after the class name. The following defines a generic class.


Syntax:

class DataStore<T> 
{     
    public T Data { get; set; } 
} 

Above, the DataStore is a generic class. T is called type parameter, which can be used as a type of fields, properties, method parameters, return types, and delegates in the DataStore class. For example, Data is generic property because we have used a type parameter T as its type instead of the specific data type.


Example: Generics Class

using System;
					
public class Program
{
	public static void Main()
	{
		DataStore<string> strStore = new DataStore<string>();
		strStore.Data = "Hello World!";
		//strStorage.Data = 123; // compile-time error
		Console.WriteLine(strStore.Data);
		
		DataStore<int> intStore = new DataStore<int>();
		intStore.Data = 100;
		//intStorage.Data = "Hello World!"; // compile-time error
		Console.WriteLine(intStore.Data);

		KeyValuePair<int, string> kvp1 = new KeyValuePair<int, string>();
		kvp1.Key = 100;
		kvp1.Value = "Hundred";
		Console.WriteLine(kvp1.Key + ", " + kvp1.Value);

		
		KeyValuePair<string, string> kvp2 = new KeyValuePair<string, string>();
		kvp2.Key = "IT";
		kvp2.Value = "Information Technology";
		Console.WriteLine(kvp2.Key + ", " + kvp2.Value);
	}
}

class DataStore<T>
{
    public T Data { get; set; }
}

class KeyValuePair<TKey, TValue>
{
    public TKey Key { get; set; }
    public TValue Value { get; set; }
} 

Output:






Generics Delegates

The Generic Delegates in C# were introduced as part of .NET Framework 3.5 which doesn’t require defining the delegate instance in order to invoke the methods. To understand the Generic Delegates in C# you should have the basic knowledge of Delegates.


Types of Generic Delegates in C#

C# provides three built-in generic delegates, they are

  1. Func

  2. Action

  3. Predicate


1. Func Generics Delegates:

The Func Generic Delegate in C# is present in the System namespace. This delegate takes one or more input parameters and returns one out parameter. The last parameter is considered as the return value. The Func Generic Delegate in C# can take up to 16 input parameters of different types. It must have one return type. The return type is mandatory but the input parameter is not.


2. Action Generic Delegate:

The Action Generic Delegate in C# is also present in the System namespace. It takes one or more input parameters and returns nothing. This delegate can take a maximum of 16 input parameters of the different or same type


3. Predicate Generic Delegate:

The Predicate Generic Delegate in C# is also present in the System namespace. This delegate is used to verify certain criteria of the method and returns the output as Boolean, either True or False. It takes one input parameter and always returns a Boolean value which is mandatory. This delegate can take a maximum of 1 input parameter and always return the value of the Boolean type.


Example : Generics Delegates

namespace GenericDelegateDemo
{
    public class GenericDelegates
    {
        static void Main(string[] args)
        {
            Func<int, float, double, double> obj1 = new Func<int, float, double, double>(AddNumber1);
            double Result = obj1.Invoke(100, 125.45f, 456.789);
            Console.WriteLine(Result);
            
            Action<int, float, double> obj2 = new Action<int, float, double>(AddNumber2);
            obj2.Invoke(50, 255.45f, 123.456);
            
            Predicate<string> obj3 = new Predicate<string>(CheckLength);
            bool Status = obj3.Invoke("Pranaya");
            Console.WriteLine(Status);

            Console.ReadKey();
        }

        public static double AddNumber1(int no1, float no2, double no3)
        {
            return no1+ no2 + no3;
        }

        public static void AddNumber2(int no1, float no2, double no3)
        {
            Console.WriteLine( no1 + no2 + no3);
        }

        public static bool CheckLength(string name)
        {
            if (name.Length > 5)
                return true;
            return false;
        }
    }
}

Source: dotnettutorials.com

Output:






Features of Generics

Generics is a technique that enriches your programs in the following ways −

  • It helps you to maximize code reuse, type safety, and performance.

  • You can create generic collection classes. The .NET Framework class library contains several new generic collection classes in the System.Collections.Generic namespace. You may use these generic collection classes instead of the collection classes in the System.Collections namespace.

  • You can create your own generic interfaces, classes, methods, events, and delegates.

  • You may create generic classes constrained to enable access to methods on particular data types.

  • You may get information on the types used in a generic data type at run-time by means of reflection.



Benefits of Using Generics

  1. Source code protection The developer using a generic algorithm doesn’t need to have access to the algorithm’s source code. With C++ templates or Java’s generics, however, the algorithm’s source code must be available to the developer who is using the algorithm.

  2. Type safety When a generic algorithm is used with a specific type, the compiler and the CLR understand this and ensure that only objects compatible with the specified data type are used with the algorithm. Attempting to use an object of an incompatible type will result in either a compiler error or a runtime exception being thrown. In the example of GenericList discussed earlier, attempting to pass a String object to the Add method results in the compiler error. Check the figure below.

  3. Cleaner code Since the compiler enforces type safety, fewer casts are required in your source code, meaning that your code is easier to write and maintain. In the last code snippet I have to cast the object type to int if I am not using generics.

  4. Better performance Before generics, the way to define a generalized algorithm was to define all of its members to work with the Object data type. If you wanted to use the algorithm with value type instances, the CLR had to box the value type instance prior to calling the members of the algorithm.


Drawback of using Generic

  1. Generic types can be derived from most base classes, such as MarshalByRefObject (and constraints can be used to require that generic type parameters derive from base classes like MarshalByRefObject). However, .NET does not support context-bound generic types. A generic type can be derived from ContextBoundObject, but trying to create an instance of that type causes a TypeLoadException.

  2. Enumerations cannot have generic type parameters. An enumeration can be generic only incidentally (for example, because it is nested in a generic type that is defined using Visual Basic, C#, or C++). For more information, see "Enumerations" in Common Type System.

  3. Lightweight dynamic methods cannot be generic.

  4. In Visual Basic, C#, and C++, a nested type that is enclosed in a generic type cannot be instantiated unless types have been assigned to the type parameters of all enclosing types. Another way of saying this is that in reflection, a nested type that is defined using these languages includes the type parameters of all its enclosing types. This allows the type parameters of enclosing types to be used in the member definitions of a nested type. For more information, see "Nested Types" in MakeGenericType.



The Tech Platform

0 comments
bottom of page