🚀 SOLID in C# — Principles That Still Matter (With Real Examples)
SOLID is often taught as theory.
In real .NET systems, it’s a practical toolkit for writing code that survives growth.
Let’s go through each principle with concrete examples — no UML, no fluff.
🧩 S — Single Responsibility Principle (SRP)
A class should have one reason to change.
❌ Too many responsibilities:
public class OrderService { public void CreateOrder(Order order) { } public void SaveToDatabase(Order order) { } public void SendEmail(Order order) { } }
✅ Split responsibilities:
public class OrderService { public void CreateOrder(Order order) { } } public class OrderRepository { public void Save(Order order) { } } public class EmailNotifier { public void Send(Order order) { } }
🎯 Result: easier testing, clearer intent, safer changes.
🧩 O — Open/Closed Principle (OCP)
Open for extension, closed for modification.
❌ Changing logic every time:
if (type == "Vip") discount = 0.2m; else discount = 0.1m;
✅ Extend behavior instead:
public interface IDiscountPolicy { decimal Apply(decimal total); } public class VipDiscount : IDiscountPolicy { public decimal Apply(decimal total) => total * 0.8m; }
🎯 Add new discounts without touching existing code.
🧩 L — Liskov Substitution Principle (LSP)
Derived types must be substitutable for their base types.
❌ Violating expectations:
public class ReadOnlyFile : File { public override void Write(string text) => throw new NotSupportedException(); }
This breaks callers relying on File.
✅ Better design:
public interface IReadFile { string Read(); } public interface IWriteFile { void Write(string text); }
🎯 If it throws “NotSupportedException”, it’s probably violating LSP.
🧩 I — Interface Segregation Principle (ISP)
Don’t force clients to depend on methods they don’t use.
❌ Fat interface:
public interface IMachine { void Print(); void Scan(); void Fax(); }
✅ Segregated interfaces:
public interface IPrinter { void Print(); } public interface IScanner { void Scan(); }
🎯 Smaller interfaces → fewer breaking changes.
🧩 D — Dependency Inversion Principle (DIP)
Depend on abstractions, not implementations.
❌ Hard dependency:
public class OrderService { private readonly SqlOrderRepository _repo = new(); }
✅ Inverted dependency:
public class OrderService { public OrderService(IOrderRepository repo) { _repo = repo; } }
🎯 Enables testing, swapping implementations, and cleaner architecture.
🧠 SOLID in Modern .NET (Reality Check)
- DI containers make D easy
- Interfaces & records simplify O and I
- Primary constructors reduce boilerplate
- Lambdas often replace small strategy classes
SOLID adapts — it doesn’t disappear.
⚠️ Common SOLID Anti-Patterns
❌ One interface per class “just in case”
❌ Over-abstraction in CRUD apps
❌ SRP applied at method level only
❌ Complex inheritance trees
❌ SOLID used as dogma instead of guidance
If SOLID makes code harder to read, you’re overdoing it.
🎯 Final Takeaway
SOLID is not about rules.
It’s about controlling change.
Good SOLID code:
- Changes in fewer places
- Breaks less often
- Is easier to test
- Is easier to explain
Write code for the next developer — and that developer is usually future you.
#dotnet #csharp #solid #softwarearchitecture #cleancode #backend #engineering