Loading...

Generics in C#

Generics in C# is a powerful programming feature that enables developers to define classes, methods, and data structures that can operate on any data type while maintaining compile-time type safety and high performance. By using generics, you can create reusable components that avoid the need to write duplicate code for different types, which reduces runtime errors and increases maintainability and scalability.
Generics are essential in scenarios such as implementing collections, algorithms, and utility libraries where multiple data types need to be handled in a consistent manner. Key C# concepts related to generics include syntax for defining generic types and methods, integration with object-oriented programming (OOP) principles, utilization of generic collections like List and Dictionary\, and algorithm design. Proper use of generics ensures type safety, avoids unnecessary casting and boxing, and optimizes performance for large-scale systems.
This tutorial will guide readers through defining generic classes and methods, applying type constraints, and implementing practical solutions that leverage generics for robust software architecture. By the end, readers will understand how to incorporate generics into real-world C# applications, design scalable and reusable components, and improve system efficiency, all while following best practices for exception handling, memory management, and coding standards.

Basic Example

text
TEXT Code
using System;

namespace GenericsDemo
{
// Generic class definition
public class GenericBox<T>
{
private T content;

public void Add(T item)
{
content = item;
}

public T Get()
{
return content;
}
}

class Program
{
static void Main(string[] args)
{
// Using generic class with int
GenericBox<int> intBox = new GenericBox<int>();
intBox.Add(100);
Console.WriteLine("Box content: " + intBox.Get());

// Using generic class with string
GenericBox<string> stringBox = new GenericBox<string>();
stringBox.Add("Hello, World");
Console.WriteLine("Box content: " + stringBox.Get());
}
}

}

The code above demonstrates the basics of generics in C#. The GenericBox class defines a type parameter T, which allows the same class to handle different data types specified at object instantiation. The private field content stores the data, and the methods Add and Get allow setting and retrieving the value.
In the Main method, GenericBox and GenericBox instances show how one generic class can manage multiple types. This approach ensures type safety, as the compiler enforces type consistency, avoiding runtime errors. Generics reduce code duplication and enhance maintainability. Using generics eliminates the need for casting, which improves performance and reduces the risk of invalid type conversions.
In practical C# projects, this pattern is useful for building reusable containers, caching systems, or algorithm libraries. Developers often ask why not just use object types; generics provide compile-time type checking, eliminate frequent boxing/unboxing, and offer higher efficiency. This foundational understanding sets the stage for applying generics in advanced algorithms and real-world system architecture.

Practical Example

text
TEXT Code
using System;
using System.Collections.Generic;

namespace AdvancedGenericsDemo
{
// Generic repository class with type constraints
public class Repository<T> where T : class
{
private List<T> items = new List<T>();

public void Add(T item)
{
if (item == null)
throw new ArgumentNullException(nameof(item), "Item cannot be null");
items.Add(item);
}

public T Find(Predicate<T> predicate)
{
return items.Find(predicate);
}

public void DisplayAll()
{
foreach (var item in items)
{
Console.WriteLine(item.ToString());
}
}
}

class Product
{
public string Name { get; set; }
public decimal Price { get; set; }

public override string ToString()
{
return $"{Name} - {Price} USD";
}
}

class Program
{
static void Main(string[] args)
{
Repository<Product> productRepo = new Repository<Product>();
productRepo.Add(new Product { Name = "Laptop", Price = 1500 });
productRepo.Add(new Product { Name = "Smartphone", Price = 800 });

Console.WriteLine("All Products:");
productRepo.DisplayAll();

Product expensiveProduct = productRepo.Find(p => p.Price > 1000);
Console.WriteLine("Most expensive product: " + expensiveProduct);
}
}

}

In this practical example, the Repository class is a generic repository that manages a collection of items with a type constraint where T : class, ensuring only reference types are accepted. The class provides methods to add items with null checks, find items using Predicate, and display all items. This guarantees robustness and type safety while reducing boilerplate code.
The Product class represents a real-world entity with Name and Price properties and overrides ToString for easy output. The Main method shows how Repository can add, display, and search for products using a price condition. This example demonstrates combining generics with OOP principles, collection algorithms, and proper exception handling. It highlights best practices for memory safety, reusability, and efficient system design, which are crucial for large-scale C# applications.

C# best practices and common pitfalls for generics include:

  • Always use type constraints to ensure valid types for generic classes and methods.
  • Avoid storing unmanaged resources inside generic containers without proper disposal to prevent memory leaks.
  • Use built-in generic collections like List and Dictionary\ for optimal performance.
  • Provide clear exception handling to handle null or invalid inputs.
  • Avoid overgeneralizing generics when unnecessary, as it can make the code harder to maintain.
  • Debugging generic code requires careful attention to the actual type parameters at runtime.
  • Optimize performance by minimizing unnecessary boxing/unboxing and leveraging type constraints.
  • Security considerations include validating input types and avoiding exposing sensitive data through generic interfaces.

📊 Reference Table

C# Element/Concept Description Usage Example
Generic Class Class that can handle multiple data types public class Box<T> { T content; }
Generic Method Method that can operate on multiple types public T GetItem<T>(T item) { return item; }
Constraints Restrict acceptable types for generics where T : class, new()
List<T> Generic list collection List<int> numbers = new List<int>();
Dictionary\<TKey,TValue> Generic key-value collection Dictionary\<string,int> ages = new Dictionary\<string,int>();
Predicate<T> Condition delegate for searching items.Find(p => p.Price > 100);

Summary and next steps:
Mastering generics in C# allows developers to write efficient, reusable, and type-safe code. By understanding generic classes, methods, collections, and constraints, you can implement complex data processing and algorithmic operations with minimal redundancy. Generics play a critical role in software architecture by promoting modularity, maintainability, and reduced runtime errors.
Next steps include exploring advanced collections like Stack and Queue, using LINQ with generics, and implementing interfaces such as IEnumerable and IComparable. Combining generics with design patterns enables the development of scalable and maintainable systems. Applying best practices in real projects will enhance code quality, performance, and reliability. Continuing learning resources include official C# documentation, open-source libraries, and advanced C# textbooks.

🧠 Test Your Knowledge

Ready to Start

Test Your Knowledge

Test your understanding of this topic with practical questions.

4
Questions
🎯
70%
To Pass
♾️
Time
🔄
Attempts

📝 Instructions

  • Read each question carefully
  • Select the best answer for each question
  • You can retake the quiz as many times as you want
  • Your progress will be shown at the top