Understanding C# Generics
Generics provide a way to create type-safe collections and methods that can operate on a variety of types without casting. This significantly improves performance and code reusability.
What are Generics?
Before generics, collections like ArrayList in C# could store objects of any type. However, retrieving an element required casting it back to its original type, which was prone to runtime errors. Generics solve this by allowing you to specify the type of data a collection or method can hold at compile time.
Benefits of Using Generics
- Type Safety: Prevents type-related errors at compile time.
- Performance: Eliminates the overhead of boxing and unboxing value types.
- Code Reusability: Allows you to write a single class or method that can be used with different types.
- Readability: Makes code clearer by explicitly stating the intended types.
Generic Classes
A generic class is a class that is parameterized by types. You define a generic class by appending type parameters in angle brackets (<T>) to the class name. The T is a placeholder for a type that will be specified when an instance of the class is created.
Example: A Simple Generic List
public class GenericList<T>
{
private T[] _items;
private int _count;
public GenericList(int capacity = 10)
{
_items = new T[capacity];
_count = 0;
}
public void Add(T item)
{
if (_count == _items.Length)
{
// Resize array if necessary (simplified for example)
System.Array.Resize(ref _items, _items.Length * 2);
}
_items[_count++] = item;
}
public T this[int index]
{
get { return _items[index]; }
set { _items[index] = value; }
}
public int Count
{
get { return _count; }
}
}
Now, you can create instances of GenericList for specific types:
Using the Generic List
// A list of integers
GenericList<int> numberList = new GenericList<int>();
numberList.Add(10);
numberList.Add(20);
int firstNumber = numberList[0]; // No casting needed
// A list of strings
GenericList<string> stringList = new GenericList<string>();
stringList.Add("Hello");
stringList.Add("World");
string s = stringList[1]; // No casting needed
Generic Methods
Methods can also be generic. This is useful for operations that are generic in nature, regardless of the class they are defined in.
Example: A Generic Swap Method
public static class GenericUtility
{
public static void Swap<T>(ref T a, ref T b)
{
T temp = a;
a = b;
b = temp;
}
}
Usage:
Using the Generic Swap Method
int x = 5, y = 10;
GenericUtility.Swap<int>(ref x, ref y); // x is now 10, y is now 5
string s1 = "Apple", s2 = "Banana";
GenericUtility.Swap<string>(ref s1, ref s2); // s1 is now "Banana", s2 is now "Apple"
In many cases, the compiler can infer the type argument, so you don't need to explicitly specify it:
GenericUtility.Swap(ref x, ref y); // Compiler infers T as int
GenericUtility.Swap(ref s1, ref s2); // Compiler infers T as string
Built-in Generic Collections
The .NET Framework provides a rich set of generic collection classes in the System.Collections.Generic namespace, such as:
List<T>: A resizable array.Dictionary<TKey, TValue>: A collection of key/value pairs.HashSet<T>: A collection of unique elements.Queue<T>: A first-in, first-out (FIFO) collection.Stack<T>: A last-in, first-out (LIFO) collection.
Conclusion
Generics are a fundamental feature in C# that significantly enhances type safety and performance. By mastering generic classes and methods, you can write more robust, efficient, and reusable code.