Page cover
SOLID isn't dogma -
it's about building maintainable systems,
flexible architectures &
code that stands the test of time.

SOLID Principles in C# with Real Examples

SOLID Principles in C# with Real Examples
C# November 20, 2024 14 min read

Understand and apply the five SOLID principles to write maintainable, extensible C# code with practical examples from real projects.

SOLID principles are fundamental guidelines for writing clean, maintainable object-oriented code. Let's explore each principle with practical C# examples.

Single Responsibility Principle (SRP)

A class should have only one reason to change. It should have only one job or responsibility.

csharp
// Bad: Multiple responsibilities
public class UserService
{
    public void CreateUser(User user)
    {
        // Create user
        SaveToDatabase(user);
        SendWelcomeEmail(user.Email);
        LogUserCreation(user.Id);
    }
}

// Good: Separated responsibilities
public class UserRepository
{
    public void Save(User user) { /* ... */ }
}

public class EmailService
{
    public void SendWelcomeEmail(string email) { /* ... */ }
}

public class UserLogger
{
    public void LogCreation(int userId) { /* ... */ }
}

public class UserService
{
    private readonly UserRepository _repository;
    private readonly EmailService _emailService;
    private readonly UserLogger _logger;

    public void CreateUser(User user)
    {
        _repository.Save(user);
        _emailService.SendWelcomeEmail(user.Email);
        _logger.LogCreation(user.Id);
    }
}

Open/Closed Principle (OCP)

Software entities should be open for extension but closed for modification.

csharp
// Good: Using abstraction for extension
public interface IPaymentProcessor
{
    void ProcessPayment(decimal amount);
}

public class CreditCardProcessor : IPaymentProcessor
{
    public void ProcessPayment(decimal amount)
    {
        // Credit card logic
    }
}

public class PayPalProcessor : IPaymentProcessor
{
    public void ProcessPayment(decimal amount)
    {
        // PayPal logic
    }
}

public class PaymentService
{
    public void Process(IPaymentProcessor processor, decimal amount)
    {
        processor.ProcessPayment(amount);
    }
}

Liskov Substitution Principle (LSP)

Objects of a superclass should be replaceable with objects of a subclass without breaking the application.

Interface Segregation Principle (ISP)

Clients should not be forced to depend on interfaces they don't use. Create specific interfaces instead of one general-purpose interface.

Dependency Inversion Principle (DIP)

csharp
// Good: Depend on abstractions
public interface IDatabase
{
    void Save(object data);
}

public class SqlDatabase : IDatabase
{
    public void Save(object data) { /* SQL logic */ }
}

public class DataService
{
    private readonly IDatabase _database;

    public DataService(IDatabase database)
    {
        _database = database;
    }

    public void SaveData(object data)
    {
        _database.Save(data);
    }
}

Applying SOLID principles leads to more maintainable, testable, and flexible code. Start with SRP and gradually incorporate the other principles as you gain experience.

Tags: C# SOLID Design Patterns Clean Code

Found this helpful?

I write about software engineering, architecture, and best practices. Check out more articles or get in touch.