C# tutorials > Core C# Fundamentals > Data Structures and Collections > What is the purpose of generics in C# Collections (`<T>`)?
What is the purpose of generics in C# Collections (`<T>`)?
Generics in C# Collections, denoted by the `<T>` syntax, provide a powerful mechanism for creating type-safe collections. Before generics, collections like `ArrayList` stored objects as type `object`, requiring casting when retrieving elements. Generics eliminate the need for this casting, improve type safety, and enhance performance. They allow you to define a collection that works with a specific type without sacrificing the flexibility of working with collections.
Core Concept: Type Safety
The primary purpose of generics is to enforce type safety at compile time. Without generics, the compiler cannot verify the type of objects being added to or retrieved from a collection. Generics allow you to specify the exact type a collection will hold, leading to fewer runtime errors and easier debugging.
Example: Non-Generic vs. Generic Collection
This code demonstrates the difference between a non-generic `ArrayList` and a generic `List<int>`. The `ArrayList` can store any type, but retrieving an element requires a cast. The `List<int>` is restricted to storing integers, so no casting is necessary, and attempts to add other types result in compile-time errors.
using System;
using System.Collections;
using System.Collections.Generic;
public class Example
{
public static void Main(string[] args)
{
// Non-generic ArrayList (requires casting)
ArrayList list = new ArrayList();
list.Add(10); // Adding an integer
list.Add("Hello"); // Adding a string
int num = (int)list[0]; // Casting required
//string str = (int)list[1]; //Runtime Exception: InvalidCastException
// Generic List<T> (no casting required)
List<int> intList = new List<int>();
intList.Add(10);
//intList.Add("Hello"); //Compile-time error!
int num2 = intList[0]; // No casting needed
Console.WriteLine(num); // Output: 10
Console.WriteLine(num2); // Output: 10
}
}
Explanation of the `T` in ``
The `T` in `<T>` is a type parameter. It's a placeholder for the specific type you want the collection to work with. You can replace `T` with any valid C# type, such as `int`, `string`, `Customer`, or even another generic type. The compiler will then substitute `T` with the specified type throughout the collection's definition. You can also use other letters but `T` is generally used by convention.
Real-Life Use Case
Imagine building an e-commerce application. You need to manage a collection of `Product` objects. Using `List<Product>` guarantees that the collection only contains `Product` instances, preventing accidental insertion of incorrect data and ensuring type safety when accessing product properties.
using System;
using System.Collections.Generic;
public class Product
{
public string Name { get; set; }
public decimal Price { get; set; }
}
public class Example
{
public static void Main(string[] args)
{
List<Product> products = new List<Product>();
products.Add(new Product { Name = "Laptop", Price = 1200.00m });
products.Add(new Product { Name = "Mouse", Price = 25.00m });
foreach (Product product in products)
{
Console.WriteLine($"{product.Name}: ${product.Price}");
}
}
}
Performance Benefits
Generics enhance performance by avoiding boxing and unboxing. Boxing occurs when a value type (e.g., `int`, `bool`) is treated as an object. Unboxing is the reverse process. Since non-generic collections store items as `object`, value types are boxed when added and unboxed when retrieved. Generics eliminate this overhead, leading to faster execution, especially when dealing with large collections of value types.
Best Practices
Interview Tip
When asked about generics, emphasize the importance of type safety, performance benefits (avoiding boxing/unboxing), and code reusability. Be prepared to explain the difference between generic and non-generic collections with examples.
When to Use Generics
Use generics whenever you need to create collections or methods that operate on specific types in a type-safe and efficient manner. They are particularly beneficial when working with value types, as they eliminate boxing and unboxing overhead.
Memory Footprint
Generics can lead to a smaller memory footprint, especially when dealing with value types. Since generics avoid boxing and unboxing, the memory required to store value types in generic collections is less than in non-generic collections.
Alternatives
The primary alternative to generics is using non-generic collections like `ArrayList` or `Hashtable`. However, this approach is generally discouraged due to the lack of type safety and the performance overhead of boxing and unboxing. Another alternative, in very specific scenarios, might involve creating specialized collections for each type. This would lead to code duplication and maintenance overhead, making generics the superior choice in most situations.
Pros
Cons
FAQ
-
What happens if I try to add the wrong type to a generic collection?
If you attempt to add an object of a type that doesn't match the type parameter of the generic collection, you will get a compile-time error. This is one of the key benefits of generics, as it catches type errors early in the development process.
-
Can I create my own generic classes and methods?
Yes, you can define your own generic classes, interfaces, structures, delegates, and methods using the `<T>` syntax. This allows you to create reusable components that work with different types.
-
Are there any limitations to what I can use as a type parameter?
Type parameters can be any valid C# type, including value types, reference types, and other generic types. You can also use constraints to restrict the types that can be used as type parameters. For example, you can specify that a type parameter must be a class, a struct, or implement a specific interface.