Technical Articles

What Are Generics and Delegates in C# .NET (Including .NET Core and Web API)?

0 0

Generics and Delegates are key features in C# .NET that improve flexibility, reusability, and maintainability of code. These concepts are also heavily used in .NET Core and Web API development for handling data structures, method delegation, event handling, and designing robust APIs.

Generics in C#

Generics allow you to define classes, methods, interfaces, or delegates with a placeholder for the data type. This improves code reusability, type safety, and performance. Generics are particularly powerful in scenarios where you need to work with multiple data types without sacrificing type safety.

Benefits of Generics:

  • Type Safety: Prevents runtime errors by catching type mismatches at compile time.
  • Reusability: One generic class or method can work with multiple types, reducing code duplication.
  • Performance: Avoids boxing and unboxing, improving performance when dealing with value types.

Example of Generics:


public class GenericClass<T> {
    private T data;

    public void SetValue(T value) {
        data = value;
    }

    public T GetValue() {
        return data;
    }
}

public class Program {
    public static void Main() {
        // Using GenericClass with int type
        GenericClass<int> intInstance = new GenericClass<int>();
        intInstance.SetValue(100);
        Console.WriteLine(intInstance.GetValue());  // Output: 100

        // Using GenericClass with string type
        GenericClass<string> stringInstance = new GenericClass<string>();
        stringInstance.SetValue("Hello");
        Console.WriteLine(stringInstance.GetValue());  // Output: Hello
    }
}

Generics in .NET Core and Web API

In .NET Core and Web API, generics are widely used in various libraries and frameworks to ensure type safety and reusability. For instance, generics are heavily used in:

  • Entity Framework Core (EF Core) for repositories and data access patterns.
  • ASP.NET Core MVC for handling typed controllers and responses.
  • Repository Pattern: Generic repositories are commonly used to standardize database access across multiple entities.
  • Dependency Injection (DI): Generic services can be injected into controllers or other services to handle logic for different data types.

Example of Generic Repository in ASP.NET Core:


public interface IRepository<T> where T : class {
    Task<IEnumerable<T>> GetAllAsync();
    Task<T> GetByIdAsync(int id);
    Task AddAsync(T entity);
    Task UpdateAsync(T entity);
    Task DeleteAsync(int id);
}

public class Repository<T> : IRepository<T> where T : class {
    private readonly DbContext _context;
    
    public Repository(DbContext context) {
        _context = context;
    }
    
    public async Task<IEnumerable<T>> GetAllAsync() {
        return await _context.Set<T>().ToListAsync();
    }

    public async Task<T> GetByIdAsync(int id) {
        return await _context.Set<T>().FindAsync(id);
    }

    public async Task AddAsync(T entity) {
        await _context.Set<T>().AddAsync(entity);
        await _context.SaveChangesAsync();
    }

    public async Task UpdateAsync(T entity) {
        _context.Set<T>().Update(entity);
        await _context.SaveChangesAsync();
    }

    public async Task DeleteAsync(int id) {
        var entity = await GetByIdAsync(id);
        _context.Set<T>().Remove(entity);
        await _context.SaveChangesAsync();
    }
}

Delegates in C#

A delegate is a type that holds a reference to methods. Delegates allow methods to be passed as parameters, making them useful for event handling, callbacks, and designing extensible and flexible systems.

Benefits of Delegates:

  • Extensibility: You can pass methods as parameters, allowing dynamic behavior modification.
  • Loose Coupling: Provides a way to call methods dynamically, which promotes loose coupling between objects.
  • Event Handling: Delegates are key in event-driven programming, commonly used in GUI and API events.

Example of Delegates:


// Declare a delegate
public delegate void DisplayMessage(string message);

public class Program {
    // Method that matches the delegate signature
    public static void ShowMessage(string message) {
        Console.WriteLine(message);
    }

    public static void Main() {
        // Create an instance of the delegate
        DisplayMessage display = new DisplayMessage(ShowMessage);
        
        // Invoke the delegate
        display("Hello, this is a delegate example!");  // Output: Hello, this is a delegate example!
    }
}

Delegates in .NET Core and Web API

In ASP.NET Core and Web API, delegates play a vital role in extending functionalities, especially for:

  • Middleware: Delegates are used to define middleware in ASP.NET Core, allowing flexible request and response pipelines.
  • Event Handling: Delegates are often used in background services or event-driven API architectures where different components need to respond to system events.

Example: Middleware with Delegate in ASP.NET Core:


public class Startup {
    public void Configure(IApplicationBuilder app) {
        // Custom middleware using a delegate
        app.Use(async (context, next) => {
            Console.WriteLine("Request handled in middleware.");
            await next.Invoke();
        });

        app.Run(async context => {
            await context.Response.WriteAsync("Hello from the API!");
        });
    }
}

Delegates in Event Handling (Web API):


public delegate void ProcessCompleted();

public class LongRunningProcess {
    public event ProcessCompleted OnProcessCompleted;

    public void StartProcess() {
        Console.WriteLine("Process started...");
        // Simulate long-running task
        Thread.Sleep(2000);
        OnProcessCompleted?.Invoke();  // Trigger event after process completes
    }
}

public class Program {
    public static void Main() {
        LongRunningProcess process = new LongRunningProcess();
        process.OnProcessCompleted += ProcessCompletedHandler;  // Subscribe to the event
        process.StartProcess();
    }

    public static void ProcessCompletedHandler() {
        Console.WriteLine("Process completed successfully.");
    }
}

Key Differences Between Generics and Delegates

Feature Generics Delegates
Purpose Define classes, methods, or interfaces that can work with any data type. Reference methods as objects and pass them as parameters.
Type Safety Provides type safety at compile time. Provides flexibility in passing methods.
Use Case Collections, methods, and interfaces. Event handling, callbacks, and extensibility.
Reusability Ensures reusability across multiple data types. Reuses methods by referencing them through delegates.
Performance Efficient due to the avoidance of boxing/unboxing. Can involve additional overhead in method invocation.

Real-World Analogy

  • Generics: Think of generics like a recipe that can be used to cook different types of dishes (data types). You can use the same recipe (generic class or method) to make pasta, curry, or any dish by simply changing the ingredients (data type).
  • Delegates: Delegates are like a remote control that can switch between different devices (methods). The same remote (delegate) can control a TV, a fan, or a stereo depending on the device (method) you link it to.

Tricky and Important Q&A

Q1: Can a delegate return a value in C#?

Answer: Yes, delegates can return values. You can declare a delegate with a return type, just like a method.


public delegate int Calculate(int x, int y);

Q2: What is the difference between Func, Action, and Predicate in C#?

    • Func: A delegate that returns a value. It can take up to 16 parameters.
Func<int, int, int> add = (x, y) => x + y;
    • Action: A delegate that does not return a value. It can take up to 16 parameters.
Action<string> print = (message) => Console.WriteLine(message);
    • Predicate: A delegate that takes one parameter and returns a boolean.
Predicate<int> isPositive = x => x > 0;

Q3: Can generics be constrained to specific data types?

Answer: Yes, you can add constraints to generic types using the where keyword, restricting the type to interfaces, classes, or structures.


public class MyClass<T> where T : class {
    // T must be a class
}

Q4: Can delegates point to multiple methods?

Answer: Yes, delegates can be multicast, meaning they can reference multiple methods. When invoked, all methods in the delegate invocation list are called.

Conclusion

Generics: Allow you to write flexible, reusable, and type-safe code. They are particularly useful in collections, methods, and interfaces, especially in .NET Core and Web API where reusability and type safety are critical.

Delegates: Represent methods as objects, enabling dynamic method invocation, event handling, and callbacks. Delegates are fundamental in ASP.NET Core middleware, event-driven programming, and designing extensible APIs.

By understanding and using generics and delegates, you can build more robust, maintainable, and flexible applications in C# .NET, including .NET Core and Web API projects.

Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x
× How can I help you?