C# Fundamentals – Generics

Tuesday, June 15th, 2021

Generics is a concept in computer programming that enables the creation of reusable, type-safe code that can work with multiple data types. It is a feature in many programming languages, including C#, Java, and C++, that provides a way to write generic algorithms and data structures that can work with multiple data types while still preserving type safety.

Generics are implemented using type parameters, which are placeholders for real data types that are specified when the generic code is used. The type parameters can be used throughout the generic code to represent the actual data types being used. When the generic code is used, the type parameters are replaced with real data types, and the resulting code is type-safe and optimized for performance.

Generics can be used to implement generic data structures, such as lists, dictionaries, and stacks, as well as generic algorithms, such as sorting and searching algorithms. They can also be used to create generic classes and methods that can be used by client code to implement custom data structures and algorithms.

In general programming theory, generics provide a way to write generic, reusable code that can work with multiple data types, while still preserving type safety and performance. This can lead to more efficient and maintainable code, as well as a reduction in the amount of code that needs to be written and maintained.

C# generics allow you to define classes, interfaces, and methods that defer the specification of one or more types until the class or method is declared and instantiated by client code. This provides a way to create reusable, type-safe code without sacrificing performance.

Generics are different from inheritance in that inheritance involves creating a new class that is a subclass of an existing class and inherits its members. Generics, on the other hand, provide a way to create classes and methods that can work with multiple types, while still preserving type safety.

Generics can reduce repeated code in C# by allowing you to write a single class or method that can work with multiple data types. This can save you the time and effort required to write separate implementations for each data type. Additionally, since generics preserve type safety, you can catch errors at compile-time, instead of runtime, which can result in more robust and efficient code.

For example, suppose you have a class that needs to store a list of objects. Without generics, you would need to write separate implementations for each type of object you want to store. With generics, you can write a single implementation that works with any type of object, which can reduce the amount of code you need to write and maintain.

Here is an example of using generics in C# to create a generic class Stack<T> that can store elements of any type:





using System;
using System.Collections.Generic;

namespace GenericsExample
{
    class Stack<T>
    {
        private List<T> elements = new List<T>();

        public void Push(T item)
        {
            elements.Add(item);
        }

        public T Pop()
        {
            if (elements.Count == 0)
            {
                throw new InvalidOperationException("The stack is empty.");
            }

            T item = elements[elements.Count - 1];
            elements.RemoveAt(elements.Count - 1);
            return item;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Stack<int> intStack = new Stack<int>();
            intStack.Push(1);
            intStack.Push(2);
            Console.WriteLine(intStack.Pop());
            Console.WriteLine(intStack.Pop());

            Stack<string> stringStack = new Stack<string>();
            stringStack.Push("Hello");
            stringStack.Push("World");
            Console.WriteLine(stringStack.Pop());
            Console.WriteLine(stringStack.Pop());
        }
    }
}

In this example, the Stack<T> class can be used to create stacks of any type, such as int or string. The type parameter T is used in the class definition to specify the type of the elements stored in the stack. When creating an instance of the stack, the type argument is provided in angle brackets, such as Stack<int> or Stack<string>.