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.
// 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.
// 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)
// 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.
Found this helpful?
I write about software engineering, architecture, and best practices. Check out more articles or get in touch.