Mastering C# Generics: A Step-by-Step Guide with Code Examples

Introduction

C# Generics allow you to define classes, methods, delegates, and interfaces that can work with any data type while providing type safety at compile-time. This feature is incredibly powerful because it enables you to create reusable and efficient code that can work with various types, without sacrificing performance or safety.

In this step-by-step guide, we will explore how C# generics work, the syntax involved, and practical code examples to help you master this essential C# feature.

What are Generics in C#?

Generics in C# allow you to design classes, methods, and interfaces that defer the specification of one or more types until the class or method is declared and instantiated by client code. Instead of creating multiple versions of a class or method to handle different types, generics let you use one generic definition that can work with any data type.

Benefits of Using Generics

  1. Code Reusability: You write generic code once, and it works with any data type.
  2. Type Safety: Compile-time type checks ensure that the types used in the generic class or method are valid.
  3. Performance: Generics avoid the need for boxing/unboxing operations when dealing with value types, unlike non-generic collections such as ArrayList.

1. Generic Classes

A generic class allows you to create a class that works with any data type. This is useful when you want to implement the same logic for different data types without duplicating code.

Code Example: Generic Class

// File: GenericClassExample.cs
using System;

namespace GenericsExample
{
    // Generic class definition
    public class GenericStorage
    {
        private T _item;

        // Method to store an item
        public void Add(T item)
        {
            _item = item;
        }

        // Method to retrieve the item
        public T GetItem()
        {
            return _item;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            // Create an instance of GenericStorage for integers
            GenericStorage intStorage = new GenericStorage();
            intStorage.Add(100);
            Console.WriteLine("Stored Integer: " + intStorage.GetItem());

            // Create an instance of GenericStorage for strings
            GenericStorage stringStorage = new GenericStorage();
            stringStorage.Add("Hello, Generics!");
            Console.WriteLine("Stored String: " + stringStorage.GetItem());
        }
    }
}

Explanation:

  • GenericStorage<T> is a generic class that works with any type T. When you instantiate this class, you specify the actual type to use, such as int or string.
  • This allows the same logic to be reused without duplicating code.

2. Generic Methods

A generic method is a method that can operate on parameters of any type. You declare the type parameter just like you would for a generic class, but only at the method level.

Code Example: Generic Method

// File: GenericMethodExample.cs
using System;

namespace GenericsExample
{
    class Program
    {
        // Generic method definition
        public static void Swap(ref T first, ref T second)
        {
            T temp = first;
            first = second;
            second = temp;
        }

        static void Main(string[] args)
        {
            int a = 10, b = 20;
            Console.WriteLine($"Before Swap: a = {a}, b = {b}");
            Swap(ref a, ref b);
            Console.WriteLine($"After Swap: a = {a}, b = {b}");

            string x = "Hello", y = "World";
            Console.WriteLine($"Before Swap: x = {x}, y = {y}");
            Swap(ref x, ref y);
            Console.WriteLine($"After Swap: x = {x}, y = {y}");
        }
    }
}

Explanation:

  • The Swap<T> method is generic and can swap values of any type, whether int or string.
  • The type parameter T is declared in the method signature and inferred from the actual types passed in when the method is called.

Conclusion

Generics are an essential feature of C# that enable you to write flexible, reusable, and type-safe code. By using generic classes, methods, and interfaces, you can create versatile solutions without compromising on performance or safety. Understanding and using generics effectively will make your C# code more efficient and maintainable.