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
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
textusing 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 GenericBoxcontent
stores the data, and the methods Add
and Get
allow setting and retrieving the value.
In the Main method, GenericBox
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
textusing 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 Repositorywhere T : class
, ensuring only reference types are accepted. The class provides methods to add items with null checks, find items using Predicate
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
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
🧠 Test Your Knowledge
Test Your Knowledge
Test your understanding of this topic with practical questions.
📝 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