| Serialization and deserialization in C# | Reflection in C# | |
🔧 Generics in C# |
Generics in C# allow you to design classes, methods, and interfaces that are reusable and type-safe without compromising performance. They use type parameters (placeholders for actual data types) that are replaced with concrete types during usage. This approach improves code reusability, safety, and efficiency compared to using the object type.
Before generics, developers often used the object type to store any data type. This caused two main issues:
int and string values in a collection was possible, leading to runtime errors.int or float) had to be boxed and unboxed when stored as objects, adding memory and CPU overhead.Generics introduce type parameters (like T), which act as placeholders for real data types. The compiler ensures type safety and generates optimized code for each data type, eliminating boxing/unboxing overhead.
A generic class is defined with one or more type parameters enclosed in angle brackets (<>) after the class name. The most common placeholder is T.
Example: A simple Box class that can hold any type of item.
// Generic class definition with a type parameter 'T'
public class Box<T>
{
private T _content;
public Box(T content)
{
_content = content;
}
public T GetContent()
{
return _content;
}
}
// Usage in the Main method
Box<int> integerBox = new Box<int>(123);
Box<string> stringBox = new Box<string>("Hello, Generics!");
int myInt = integerBox.GetContent(); // No casting needed
string myString = stringBox.GetContent(); // No casting needed
Use code with caution.
Generic methods define their own type parameters and can work independently of whether they are inside a generic class.
Example: A generic method to swap two values of any type.
public static void Swap<T>(ref T x, ref T y)
{
T temp = x;
x = y;
y = temp;
}
// Usage in the Main method
int a = 1, b = 2;
Swap<int>(ref a, ref b); // Swap two integers
Console.WriteLine($"a: {a}, b: {b}"); // Output: a: 2, b: 1
string c = "First", d = "Second";
Swap<string>(ref c, ref d); // Swap two strings
Console.WriteLine($"c: {c}, d: {d}"); // Output: c: Second, d: First
Use code with caution.
Constraints restrict what types can be used as type parameters. You can enforce rules like requiring a type to be a class, a struct, or to implement an interface.
Example: A generic method requiring the type to be a class implementing IComparable<T>.
public T FindMax<T>(T[] array) where T : class, IComparable<T>
{
T max = array[0];
foreach (T item in array)
{
if (item.CompareTo(max) > 0)
{
max = item;
}
}
return max;
}
Use code with caution.
Generics allow you to define classes, methods, interfaces, and delegates with a placeholder for the data type. This enables type-safe, reusable code without sacrificing performance or flexibility.
public class Box{ public T Value { get; set; } } var intBox = new Box { Value = 10 }; var stringBox = new Box { Value = "Hello" };
public static void Swap(ref T a, ref T b) { T temp = a; a = b; b = temp; } int x = 1, y = 2; Swap(ref x, ref y); // x = 2, y = 1
public interface IRepository{ void Add(T item); T Get(int id); }
List<T>, Dictionary<TKey, TValue>, Queue<T>, Stack<T>Func<T>, Action<T>, Predicate<T>IEnumerable<T>, IComparer<T>, IEquatable<T>T, TKey, TValue.where constraints to restrict type parameters.List<T> over ArrayList).where T : class or where T : new() for constructor or nullability constraints.Generics in C# are a powerful feature that promote type safety, reusability, and performance. Whether you're building collections, utilities, or APIs, generics help you write cleaner and more maintainable code.
| Serialization and deserialization in C# | Reflection in C# | |