Generics in C#

Generics in C# with Examples

In this article, I am going to discuss how to implement Generics in C# with Examples. Please read our previous article where we discussed the Generic Collection in C#. In C#, generic means not specific to a particular data type. C# allows us to define generic classes, interfaces, abstract classes, fields, methods, static methods, properties, events, delegates, and operators using the type parameter and without the specific data type. A type parameter is a placeholder for a particular type specified when creating an instance of the generic type. A generic type is declared by specifying a type parameter in an angle brackets after a type name, e.g. TypeName<T> where T is a type parameter. As part of this article, we are going to discuss the following pointers. 

  1. Why do we need generics in C#?
  2. What are Generics in C#?
  3. Advantages of Generics in C#.
  4. How to implement Generics in C# with Examples?
  5. How to use Generics with class and its Members in C#?
Why do we need Generics in C#?

Generic is a concept that allows us to define classes and methods with placeholders. C# compiler replaces these placeholders with the specified type at compile time. The concept of generics is used to create general-purpose classes and methods.

Generics introduces the concept of type parameters to .NET, which makes it possible to design classes and methods that defer the specification of one or more types until the class or method is declared and instantiated by client code. For example, by using a generic type parameter T, you can write a single class that other client codes can use without incurring the cost or risk of runtime casts or boxing operations.

Let us understand the need for Generics in C# with one example. Let us create a simple program to check whether two integer numbers are equal or not. The following code implementation is very straightforward. Here we created two classes with the name ClsCalculator and ClsMain. Within the ClsCalculator class, we have AreEqual() method which takes two integer values as the input parameter and then it checks whether the two input values are equal or not. If both are equal then it returns true else it will return false. And from the ClsMain class, we are calling the static AreEqual() method and showing the output based on the return value.

namespace GenericsDemo
{
    public class ClsMain
    {
        private static void Main()
        {
            bool IsEqual = ClsCalculator.AreEqual(10, 20);
            if (IsEqual)
            {
                Console.WriteLine("Both are Equal");
            }
            else
            {
                Console.WriteLine("Both are Not Equal");
            }

            Console.ReadKey();
        }
    }

    public class ClsCalculator
    {
        public static bool AreEqual(int value1, int value2)
        {
            return value1 == value2;
        }
    }
}

The above AreEqual() method works as expected as it is and more importantly it will only work with the integer values as this is our initial requirement. Suppose our requirement changes, now we also need to check whether two string values are equal or not.

In the above example, if we try to pass values other than the integer values, then we will get a compile-time error. This is because the AreEqual() method of the ClsCalculator class is tightly bounded with the integer data type and hence it is not possible to invoke the AreEqual method other than the integer data type values. So, when we try to invoke the AreEqual() method by passing string values as shown below we get a compile-time error.

bool Equal = ClsCalculator.AreEqual(“ABC”, “XYZ”);

One of the ways to make the above AreEqual() method to accepts string type values as well as integer type values, we need to make use of the object data type as the parameters. If we make the parameters of the AreEqual() method as Object type, then it is going to work with any data type.

Note: The most important point that you need to keep in remember is every .NET data type whether it is a primitive type or reference type, directly or indirectly inherits from the System.Object data type.

Modifying the Method to accept any data type values:

Let’s modify the AreEqual() method of the ClsCalculator class to use the Object data type as shown below.

namespace GenericsDemo
{
    public class ClsMain
    {
        private static void Main()
        {
           // bool IsEqual = ClsCalculator.AreEqual(10, 20);
            bool IsEqual = ClsCalculator.AreEqual("ABC", "ABC");
            if (IsEqual)
            {
                Console.WriteLine("Both are Equal");
            }
            else
            {
                Console.WriteLine("Both are Not Equal");
            }
            Console.ReadKey();
        }
    }

    public class ClsCalculator
    {
        //Now this method can accept any data type
        public static bool AreEqual(object value1, object value2)
        {
            return value1 == value2;
        }
    }
}

That’s it. Run the application and you will see it is working as expected. Let’s see the problem of the above code implementation.

  1. We get poor Performance due to boxing and unboxing. The object type needs to be converted to the value type.
  2. Now, the AreEuqal() method is not type-safe. Now it is possible to pass a string value for the first parameter and an integer value for the second parameter. 
Method Overloading to Achieve the same:

Another option is we need to overload the AreEqual method which will accept different types of parameters as shown below. As you can see in the below code, now we have created three methods with the same name but with different types of parameters. This is nothing but method overloading. Now, run the application and you will see everything is working as expected.

namespace GenericsDemo
{
    public class ClsMain
    {
        private static void Main()
        {
           // bool IsEqual = ClsCalculator.AreEqual(10, 20);
           // bool IsEqual = ClsCalculator.AreEqual("ABC", "ABC");
            bool IsEqual = ClsCalculator.AreEqual(10.5, 20.5);

            if (IsEqual)
            {
                Console.WriteLine("Both are Equal");
            }
            else
            {
                Console.WriteLine("Both are Not Equal");
            }

            Console.ReadKey();
        }
    }

    public class ClsCalculator
    {
        public static bool AreEqual(int value1, int value2)
        {
            return value1 == value2;
        }

        public static bool AreEqual(string value1, string value2)
        {
            return value1 == value2;
        }

        public static bool AreEqual(double value1, double value2)
        {
            return value1 == value2;
        }
    }
}

The problem with the above code implementation is that we are repeating the same logic in each and every method. However, if tomorrow we need to compare two float or two long values then again we need to create two more methods.

How to solve the above Problems?

We can solve all the above problems with Generics in C#. With generics, we will make the AreEqual() method to works with different types of data types. Let us first modify the code implementation to use the generics and then we will discuss how it works.

namespace GenericsDemo
{
    public class ClsMain
    {
        private static void Main()
        {
            //bool IsEqual = ClsCalculator.AreEqual<int>(10, 20);
            //bool IsEqual = ClsCalculator.AreEqual<string>("ABC", "ABC");
            bool IsEqual = ClsCalculator.AreEqual<double>(10.5, 20.5);
            if (IsEqual)
            {
                Console.WriteLine("Both are Equal");
            }
            else
            {
                Console.WriteLine("Both are Not Equal");
            }
            Console.ReadKey();
        }
    }

    public class ClsCalculator
    {
        public static bool AreEqual<T>(T value1, T value2)
        {
            return value1.Equals(value2);
        }
    }
}

Here in the above example, in order to make the AreEqual() method generic (generic means the same method will work with the different data types), we specified the type parameter T using the angular brackets <T>. Then we use that type as the data type for the method parameters as shown in the below image.

Generics in C#

At this point, if you want to invoke the above AreEqual() method, then you need to specify the data type on which the method should operate. For example, if you want to work with integer values, then you need to invoke the AreEqual() method by specifying int as the data type as shown in the below image using angular brackets.

Invoking Generic Methods in C#

The above AreEqual() generic method is working as follows:

How the Generic Methods work in C#

If you want to work with the string values, then you need to call the AreEqual() method as shown below.
bool IsEqual= ClsCalculator.AreEqual<string>(“ABC”, “ABC”);
Now, I hope you understand the need and importance of Generics in C#.

What are Generics in C#?

As we already discussed in our previous article, the Generics in C# are introduced as part of C# 2.0. The Generics in C# allow us to define classes and methods which are decoupled from the data type. In other words, we can say that the Generics allow us to create classes using angular brackets for the data type of its members. At compilation time, these angular brackets are going to be replaced with some specific data types. In C#, the Generics can be applied to the following:

  1. Interface
  2. Abstract class
  3. Class
  4. Method
  5. Static method
  6. Property
  7. Event
  8. Delegates
  9. Operator
Advantages of Generics in C#
  1. It Increases the reusability of the code. We can use a single generic type definition for multiple purposes in the same code without any alterations. For example, you can create a generic method to add two numbers. This method can be used to add two integers as well as two float numbers without any modification to the code.
  2. The Generics are type-safe. Generic data types provide better type safety, especially in the case of collections. When using generics you need to define the type of objects to be passed to a collection. This helps the compiler to ensure that only those object types that are defined in the definition can be passed to the collection. We will get the compile-time error if we try to use a different type of data rather than the one we specified in the definition.
  3. Generic types provide better performance as compared to normal system types because they reduce the need for boxing, unboxing, and typecasting of variables or objects.
How to use Generics with Class and its Members in C#?

Let us create a generic class with a generic constructor, generic member variable, generic property, and a generic method as shown below.

using System;

namespace GenericsDemo
{
    //MyGenericClass is a Generic Class
    class MyGenericClass<T>
    {
        //Generic variable
        //The data type is generic
        private T genericMemberVariable;

        //Generic Constructor
        //Constructor accepts one parameter of Generic type
        public MyGenericClass(T value)
        {
            genericMemberVariable = value;
        }

        //Generic Method
        //Method accepts one Generic type Parameter
        //Method return type also Generic
        public T genericMethod(T genericParameter)
        {
            Console.WriteLine("Parameter type: {0}, value: {1}", typeof(T).ToString(), genericParameter);
            Console.WriteLine("Return type: {0}, value: {1}", typeof(T).ToString(), genericMemberVariable);
            return genericMemberVariable;
        }

        //Generic Property
        //The data type is generic
        public T genericProperty { get; set; }
    }
}

In the above example, we created the class MyGenericClass with <T>. The angular brackets (“<>”) indicate that the MyGenericClass class is a generic class and the type for this class is going to be defined later.

While creating the instance of this MyGenericClass class, we need to specify the type and the compiler will assign that type to T. In the following example, we use int as the data type:

class Program
{
    static void Main()
    {
        MyGenericClass<int> integerGenericClass = new MyGenericClass<int>(10);

        int val = integerGenericClass.genericMethod(200);
        Console.ReadKey();
    }
}

Run the application and it will give you the following output.

Generics in C#

The following diagram shows how the T will be replaced with the int data type by the compiler.

Generics in C#

The compiler will compile the above class as shown in the below image

C# Generics Example

At the time of instantiation, we can use any type as per our requirement. If we want to use a string type, then we need to instantiate the class as shown below

class Program
{
    static void Main()
    {
        MyGenericClass<string> stringGenericClass = new MyGenericClass<string>("Hello Generic World");
        stringGenericClass.genericProperty = "This is a generic property example.";
        string result = stringGenericClass.genericMethod("Generic Parameter");
        Console.ReadKey();
    }
}
Output:

Generics in C#

Generic Class Characteristics in C#:
  1. A generic class increases the reusability. The more type parameters mean more reusable it becomes. However, too much generalization makes code difficult to understand and maintain.
  2. A generic class can be a base class for other generic or non-generic classes or abstract classes.
  3. A generic class can be derived from other generic or non-generic interfaces, classes, or abstract classes.

Now, I hope you understand the concept of Generics in C#. The generics are extremely used by the collection classes which belong to System.Collections.Generic namespace. Now, let us see a few examples to understand how to use Generics with class, fields, and methods in C#.

Generic Class Example in C#

The following example shows how to create a generic class using type parameter (T) with angle (<>) brackets in c# language.

using System;
namespace GenericsDemo
{
    public class GenericClass<T>
    {
        public T Message;
        public void GenericMethod(T Name, T Location)
        {
            Console.WriteLine($"Message: {Message}");
            Console.WriteLine($"Name: {Name}");
            Console.WriteLine($"Location: {Location}");
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Generics Class Example");
            // Instantiate GenericClass, string is the type argument
            GenericClass<string> myGenericClass = new GenericClass<string>();

            myGenericClass.Message = "Welcome to DotNetTutorials";
            myGenericClass.GenericMethod("Anurag Mohanty", "Bhubaneswar");
            Console.ReadLine();
        }
    }
}

Now, when you execute the above code, you will get the following output.

Generics Class Example in C#

Generic Method Example in C#:

It is also possible in C# to define a method as generic. In that case, we need to call the generic method passing any type of argument. For a better understanding, please have a look at the below example which shows how to create and call a Generic Method in C#.

using System;
namespace GenericsDemo
{
    public class SomeClass
    {
        public void GenericMethod<T>(T Param1, T Param2)
        {
            Console.WriteLine($"Parameter 1: {Param1} : Parameter 2: {Param2}");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Generics Method Example");
            SomeClass s = new SomeClass();
            //While calling the method we need to specify the type
            s.GenericMethod<int>(10, 20);
            s.GenericMethod(10.5, 20.5);
            s.GenericMethod("Anurag", "Mohanty");
            Console.ReadLine();
        }
    }
}

If you observe the above code, here, we call our generic method GenericMethod<T> with or without type parameters and send different types of arguments based on our requirements. Now, when you execute the above code, you will get the following output.

Generic Method Example in C#

Generic Field Example in C#:

A generic class can include generic fields. However, it cannot be initialized. For a better understanding, please have a look at the below example which shows how to create and use a Generic Field in C#.

using System;
namespace GenericsDemo
{
    public class GenericClass<T>
    {
        public GenericClass(T item, string name)
        {
            Item = item;
            Name = name;
        }
        public T Item { get; }
        public string Name { get; }
    }

    class Program
    {
        static void Main()
        {
            var a = new GenericClass<int>(100, "One Hundred");
            Console.WriteLine($"{a.Item} : {a.Name}");
            var b = new GenericClass<string>("Dot Net Tutorails", "Welcome to C#.NET");
            Console.WriteLine($"{b.Item} : {b.Name}");
            Console.ReadKey();
        }
    }
}

When you execute the above code, you will get the following output.

Generic Field Example in C#

In the next article, I am going to discuss the Generic Constraints in C# with Examples. In this article, I try to explain Generics in C# with Examples. I hope this Generics in C# with Examples article will help you with your needs. I would like to have your feedback. Please post your feedback, question, or comments about this article.

6 thoughts on “Generics in C#”

  1. blank

    Your explanation is good .One observation ,method overloading can be achieved within same classes and not in different classes.

Leave a Reply

Your email address will not be published.