Generics in C# Programming


Generics in C# allow you to define type-safe data structures and methods. They provide better performance and reusability by enabling the creation of classes, methods, and interfaces that work with any data type while maintaining type safety at compile time.

1. Generic Classes and Methods

Generic classes and methods allow you to define a class or method with a placeholder for the type of data it stores or operates on.

Step-by-Step Example: Generic Classes

    using System;
   // Generic class
    class GenericStorage {
        private T item;
       public void StoreItem(T value) {
            item = value;
        }
       public T RetrieveItem() {
            return item;
        }
    }
   class Program {
        static void Main() {
            // Create instances of GenericStorage for different types
            GenericStorage intStorage = new GenericStorage();
            intStorage.StoreItem(42);
            Console.WriteLine("Stored integer: " + intStorage.RetrieveItem());
           GenericStorage stringStorage = new GenericStorage();
            stringStorage.StoreItem("Hello Generics");
            Console.WriteLine("Stored string: " + stringStorage.RetrieveItem());
        }
    }
        

Output:

Stored integer: 42
Stored string: Hello Generics

Step-by-Step Example: Generic Methods

    using System;
   class Program {
        // Generic method
        static void Display(T value) {
            Console.WriteLine("Value: " + value);
        }
       static void Main() {
            Display(100);       // Using an int
            Display("Generics"); // Using a string
            Display(3.14);      // Using a double
        }
    }
        

Output:

Value: 100
Value: Generics
Value: 3.14

2. Constraints in Generics

Constraints allow you to specify the types that can be used with a generic class or method. This ensures type safety and allows access to specific features of the constrained type.

Types of Constraints

  • where T : struct - T must be a value type.
  • where T : class - T must be a reference type.
  • where T : new() - T must have a parameterless constructor.
  • where T : BaseClass - T must inherit from BaseClass.
  • where T : interface - T must implement the specified interface.

Step-by-Step Example: Constraints in Generics

    using System;
   // Generic class with constraints
    class GenericCalculator where T : struct {
        public T Add(T a, T b) {
            dynamic x = a;
            dynamic y = b;
            return x + y;
        }
    }
   class Program {
        static void Main() {
            // Create an instance of GenericCalculator for integers
            GenericCalculator intCalculator = new GenericCalculator();
            Console.WriteLine("Sum of integers: " + intCalculator.Add(10, 20));
           // Create an instance of GenericCalculator for doubles
            GenericCalculator doubleCalculator = new GenericCalculator();
            Console.WriteLine("Sum of doubles: " + doubleCalculator.Add(5.5, 3.3));
        }
    }
        

Output:

Sum of integers: 30
Sum of doubles: 8.8

Example: Using Multiple Constraints

    using System;
   // Generic class with multiple constraints
    class GenericRepository where T : class, new() {
        public T CreateInstance() {
            return new T();
        }
    }
   class Program {
        static void Main() {
            GenericRepository repository = new GenericRepository();
            MyClass instance = repository.CreateInstance();
            Console.WriteLine("Instance created: " + instance.Name);
        }
    }
   class MyClass {
        public string Name { get; set; } = "Default Name";
    }
        

Output:

Instance created: Default Name

Conclusion

Generics in C# offer flexibility and type safety for creating reusable code. Generic classes and methods are powerful tools, and constraints ensure that the types used meet specific requirements. Understanding and using generics effectively can greatly enhance the efficiency of your C# programs.




Advertisement