# Mastering C# Part 4 Generics

# Generics

Generics in C# are a fundamental feature that enhances code reusability, type safety, and performance. By allowing developers to define classes, methods, and interfaces with type parameters, generics facilitate the creation of flexible and maintainable code. This article delves into the various aspects of generics in C#, illustrating their advantages and providing practical examples.

## 1\. Why Use Generics?

**Type Safety**: Generics ensure that type correctness is enforced at compile time, reducing runtime errors. This means that any type mismatches can be caught early in the development process.

**Reusability**: With generics, you can write code that works with any data type without needing to duplicate logic for each type. This leads to cleaner and more maintainable code.

**Performance**: Generics eliminate the overhead associated with boxing/unboxing and type casting, resulting in more efficient execution of your applications.

### Example Without Generics:

```csharp
ArrayList list = new ArrayList();
list.Add(10); // Adding integer
int value = (int)list[0]; // Typecasting required
```

### Example With Generics:

```csharp
List<int> list = new List<int>();
list.Add(10); // Adding integer
int value = list[0]; // No typecasting required
```

## 2\. Generic Syntax

### Generic Classes

You can define a class using a type parameter `T`:

```csharp
class GenericClass<T>
{
    public T Data { get; set; }

    public void Display(T data)
    {
        Console.WriteLine($"Data: {data}");
    }
}
```

**Usage**:

```csharp
var intInstance = new GenericClass<int>();
intInstance.Display(42);

var stringInstance = new GenericClass<string>();
stringInstance.Display("Hello Generics");
```

### Generic Methods

Methods can also be made generic independently of their enclosing class:

```csharp
class Example
{
    public void Print<T>(T value)
    {
        Console.WriteLine($"Value: {value}");
    }
}
```

**Usage**:

```csharp
var obj = new Example();
obj.Print<int>(100);
obj.Print<string>("C# Generics");
```

## 3\. Constraints in Generics

Constraints allow you to restrict the types that can be used as type arguments, enhancing control over generic classes and methods.

### Types of Constraints

| Constraint | Description |
| --- | --- |
| `where T : class` | Restricts `T` to reference types. |
| `where T : struct` | Restricts `T` to value types. |
| `where T : new()` | Ensures `T` has a parameterless constructor. |
| `where T : baseType` | `T` must inherit from a specified base type. |
| `where T : interface` | `T` must implement a specified interface. |

### Example:

```csharp
class GenericConstraintExample<T> where T : class, new()
{
    public T CreateInstance()
    {
        return new T(); // Ensures T has a parameterless constructor
    }
}
```

## 4\. Generic Interfaces

Interfaces can also be generic, allowing for more flexible designs:

```csharp
interface IGenericRepository<T>
{
    void Add(T item);
    T Get(int id);
}

class Repository<T> : IGenericRepository<T>
{
    private List<T> items = new List<T>();

    public void Add(T item)
    {
        items.Add(item);
    }

    public T Get(int id)
    {
        return items[id];
    }
}
```

**Usage**:

```csharp
var repo = new Repository<string>();
repo.Add("Item1");
Console.WriteLine(repo.Get(0)); // Output: Item1
```

## 5\. Generic Delegates

C# includes built-in generic delegates such as **Func**, **Action**, and **Predicate**, which facilitate functional programming patterns.

### Custom Generic Delegate

```csharp
delegate T MyDelegate<T>(T value);

class Program
{
    static void Main()
    {
        MyDelegate<int> square = x => x * x;
        Console.WriteLine(square(5)); // Output: 25
    }
}
```

## 6\. Generic Collections

The .NET framework provides several generic collection types in the `System.Collections.Generic` namespace, including:

* **List**: A dynamic array.
    
* **Dictionary&lt;TKey, TValue&gt;**: A collection of key-value pairs.
    
* **Queue**: A first-in-first-out (FIFO) collection.
    
* **Stack**: A last-in-first-out (LIFO) collection.
    
* **HashSet**: A collection of unique elements.
    

### Example: Using a Generic List

```csharp
List<int> numbers = new List<int> { 1, 2, 3 };
numbers.Add(4);
numbers.ForEach(Console.WriteLine);
```

## 7\. Covariance and Contravariance in Generics

### Covariance (`out` keyword)

Covariance allows a generic type parameter to be substituted with a derived type.

```csharp
interface ICovariant<out T> { }
ICovariant<object> obj = new Covariant<string>();
```

### Contravariance (`in` keyword)

Contravariance allows a generic type parameter to be substituted with a base type.

```csharp
interface IContravariant<in T> { }
IContravariant<string> str = new Contravariant<object>();
```

## 8\. Real-World Example: Repository Pattern

The repository pattern is commonly used for implementing CRUD operations using generics:

```csharp
interface IRepository<T>
{
    void Add(T entity);
    T GetById(int id);
    IEnumerable<T> GetAll();
    void Remove(int id);
}

class Repository<T> : IRepository<T>
{
    private readonly List<T> _data = new List<T>();

    public void Add(T entity) => _data.Add(entity);
    
    public T GetById(int id) => _data[id];
    
    public IEnumerable<T> GetAll() => _data;
    
    public void Remove(int id) => _data.RemoveAt(id);
}
```

**Usage**:

```csharp
var repo = new Repository<string>();
repo.Add("Entity1");
Console.WriteLine(repo.GetById(0)); // Output: Entity1
```

## 9\. Generic Classes with Multiple Parameters

Generics can also accommodate multiple type parameters for greater flexibility:

```csharp
class Pair<T1, T2>
{
    public T1 First { get; set; }
    public T2 Second { get; set; }
}
```

**Usage**:

```csharp
var pair = new Pair<int, string> { First = 1, Second = "One" };
Console.WriteLine($"Pair: {pair.First}, {pair.Second}");
```

## 10\. Advanced Generics: Generic Factories

Generic factories allow for the creation of instances of types that have parameterless constructors:

```csharp
class Factory<T> where T : new()
{
    public T Create() => new T();
}

class Product { }

class Program
{
    static void Main()
    {
        var factory = new Factory<Product>();
        var product = factory.Create();
        Console.WriteLine(product.GetType().Name); // Output: Product
    }
}
```

Generics in C# are essential for writing reusable, type-safe, and high-performance code. By leveraging the advanced features of generics, developers can create efficient and flexible applications across various domains. Understanding generics not only enhances your coding skills but also enables you to design robust systems that can adapt to changing requirements while maintaining clarity and efficiency in your codebase.
