Explore advanced topics like delegates, events, generics, exception handling, and lambda expressions to write efficient, elegant C# code.
Welcome to our guide on Delegates, Events, and Lambdas in C#! These powerful features are essential for modern C# development. Let's dive into how they work and why they matter.
A delegate is a type that represents references to methods with a particular parameter list and return type. Think of it as a pointer to a method.
// Delegate declaration
public delegate void MyDelegate(int value);
// Method matching the delegate signature
void MyMethod(int x) {
Console.WriteLine(x);
}
// Assigning the method to the delegate
MyDelegate del = new MyDelegate(MyMethod);
A multicast delegate can point to more than one method. When invoked, it executes all its registered methods in the order they were added.
MyDelegate del = new MyDelegate(MyMethod);
del += AnotherMethod;
del -= AnotherMethod;
An event is a way for an object to communicate with other objects when something significant happens. Events are based on delegates but provide better encapsulation.
// Event declaration
public event MyDelegate SomethingHappened;
// Raising the event
protected virtual void OnSomethingHappened(int value) {
SomethingHappened?.Invoke(value);
}
A lambda expression is an anonymous method defined using concise syntax. They are particularly useful for creating delegates and event handlers.
// Lambda as a delegate
MyDelegate del = x => Console.WriteLine(x);
del(42);
Lambda expressions are widely used in LINQ (Language Integrated Query) to define queries that filter, order, and project data.
// LINQ query with lambda
var evenNumbers = numbers.Where(n => n % 2 == 0);
Delegates, events, and lambdas are used in various scenarios: - UI event handling (e.g., button click events) - Asynchronous programming - Data processing with LINQ - Frameworks like ASP.NET Core
Welcome to our chapter on Generics! In this section, we'll explore how to create reusable and type-safe components in C#. We'll also dive into constraints that allow you to enforce specific types for your generic code.
Generics provide a way to create classes, interfaces, and methods that work with any data type. They ensure compile-time type safety while maintaining flexibility.
// Without generics
collection.AddItem(123);
// With generics
collection.Add<string>('Hello');
// Example with struct constraint
class Box<T> where T : struct {
public T Value { get; set; }
}
// Example with inheritance constraint
interface IShape {}
class ShapeProcessor<T> where T : IShape {}
Exception handling is a critical aspect of robust software development. It allows you to handle unexpected errors and ensure your application remains stable even when things go wrong.
try
{
// Code that might throw an exception
}
catch (Exception ex)
{
// Handle the exception
}
finally
{
// Cleanup code that always executes
}
You can catch multiple types of exceptions using multiple catch blocks
or by catching base exception classes.
try
{
// Code that might throw an exception
}
catch (ArgumentNullException ex)
{
// Handle null argument specific error
}
catch (FormatException ex)
{
// Handle format validation errors
}
catch (Exception ex)
{
// Catch any other exceptions
}
Create custom exceptions by inheriting from the Exception class
or one of its derivatives.
public class InvalidDataException : Exception
{
public InvalidDataException(string message) : base(message)
{
}
}
// Throwing custom exception
throw new InvalidDataException("Invalid data format provided");
Defensive programming involves writing code that anticipates potential issues and handles them proactively. This includes using guard clauses
to validate inputs before proceeding.
// Guard clause example
public void ProcessData(string input)
{
if (string.IsNullOrEmpty(input))
{
throw new ArgumentNullException(nameof(input), "Input cannot be null or empty.");
}
// Proceed with processing valid data
}
Implement defensive programming by checking preconditions, postconditions, and invariants to ensure your code behaves as expected under all circumstances.
// Example of validating method arguments
public void CalculateAverage(int[] numbers)
{
if (numbers == null || numbers.Length == 0)
{
throw new ArgumentException("Numbers array must contain at least one element.", nameof(numbers));
}
double average = numbers.Average();
Console.WriteLine($"The average is: {average}");
}
Question 1 of 15