Absolutely! Here's a clear breakdown of the SOLID principles with real-life C# / .NET examples
S — Single Responsibility Principle (SRP)
A class should have only one reason to change.
Do one thing, and do it well.
Bad Example:
public class InvoiceService
{
public void GenerateInvoice() { /* ... */ }
public void SaveToDatabase() { /* ... */ }
public void SendEmail() { /* ... */ } // too many responsibilities
}
Good Example:
public class InvoiceGenerator { public void Generate() { } }
public class InvoiceRepository { public void Save() { } }
public class EmailService { public void SendEmail() { } }
O — Open/Closed Principle (OCP)
Software entities should be open for extension, but closed for modification.
Extend behavior without changing existing code.
Bad Example:
public class Report
{
public void GenerateReport(string type)
{
if (type == "PDF") { /* PDF logic */ }
else if (type == "Excel") { /* Excel logic */ }
}
}
Good Example (using abstraction):
public interface IReportGenerator { void Generate(); }
public class PdfReport : IReportGenerator { public void Generate() { } }
public class ExcelReport : IReportGenerator { public void Generate() { } }
public class ReportService
{
public void GenerateReport(IReportGenerator generator) => generator.Generate();
}
L — Liskov Substitution Principle (LSP)
Objects of a superclass should be replaceable with objects of its subclasses without breaking the application.
A derived class should honor the behavior of its base class.
Bad Example:
public class Bird { public virtual void Fly() { } }
public class Ostrich : Bird
{
public override void Fly() => throw new NotImplementedException();
}
Ostrich can’t fly, but the base class assumes all birds can — violates LSP.
Good Example:
public abstract class Bird { }
public interface IFlyingBird { void Fly(); }
public class Sparrow : Bird, IFlyingBird { public void Fly() { } }
public class Ostrich : Bird { }
I — Interface Segregation Principle (ISP)
Clients should not be forced to depend on interfaces they do not use.
Prefer many small interfaces over one large one.
Bad Example:
public interface IWorker
{
void Work();
void Eat();
}
public class Robot : IWorker
{
public void Work() { }
public void Eat() => throw new NotImplementedException(); // ISP violation
}
Good Example:
public interface IWorkable { void Work(); }
public interface IFeedable { void Eat(); }
public class Human : IWorkable, IFeedable { public void Work() { } public void Eat() { } }
public class Robot : IWorkable { public void Work() { } }
D — Dependency Inversion Principle (DIP)
High-level modules should not depend on low-level modules. Both should depend on abstractions.
Rely on interfaces, not concrete classes.
Bad Example:
public class FileLogger
{
public void Log(string message) { }
}
public class AuthService
{
private FileLogger logger = new FileLogger(); // tightly coupled
}
Good Example:
public interface ILogger
{
void Log(string message);
}
public class FileLogger : ILogger
{
public void Log(string message) { }
}
public class AuthService
{
private readonly ILogger _logger;
public AuthService(ILogger logger) => _logger = logger;
}
Now you can inject any logger (e.g.,
DbLogger
,ConsoleLogger
) without modifyingAuthService
.
Summary Table
Principle | Goal | .NET Example |
---|---|---|
SRP | One class = one job | Separate EmailService , InvoiceService |
OCP | Extend without modify | IReportGenerator interface |
LSP | Substitutable behavior | Avoid base classes with unsupported methods |
ISP | Small, focused interfaces | IWorkable , IFeedable |
DIP | Depend on abstractions | Use ILogger , inject via constructor |
Let me know if you'd like:
- A full SOLID-based project structure
- Real-world Web API or ASP.NET MVC examples using these principles
Leave Comment