Strategies to Eliminate If-Else Chains in C#
In software engineering, excessive if-else or switch statements—often referred to as "Arrow Code" due to their indentation shape—are a major source of technical debt. While conditional logic is necessary, relying on it for core business flow violates the Open/Closed Principle (OCP).
When a new requirement arrives (e.g., a new payment method or a new user type), you shouldn't have to modify existing, tested methods. You should be able to extend the system by adding new code. This article explores three robust C# patterns to replace branching logic: Interface Injection, Delegate Dictionaries, and Polymorphism.
Problem Definition
The core problem isn't the if keyword itself, but the coupling it creates. When a central method controls logic for every variation of a type, that method becomes a "God Method."
Symptoms of Bad Branching:
- High Cyclomatic Complexity: Hard to test every path.
- Fragility: A change in one branch might accidentally break another.
- Merge Conflicts: Multiple developers modifying the same massive
switchblock.
Solution 1: Interface Injection (Strategy Pattern)
The Strategy Pattern is ideal when you have a family of algorithms (behaviors) and you want to make them interchangeable. Instead of the class deciding how to do something based on an enum or string, it delegates the task to an interface.
Scenario: Notification System
❌ The Bad Code
The NotificationService knows too much about the specific implementation details of every provider.
✅ The Good Code
We define an INotifier interface. The NotificationService relies on abstraction, not implementation.
Solution 2: Delegates & Dictionary (Dispatch Table)
If the logic is lightweight (e.g., simple calculations, returning strings, parsing), creating a full class/interface structure might be overkill. A Dictionary mapping a key to a Func or Action delegate provides $O(1)$ lookup performance and cleaner syntax.
Scenario: Order Status Messaging
❌ The Bad Code
A classic switch statement that grows vertically with every new status.
✅ The Good Code
We replace the control flow with a data structure. This is often called a "Dispatch Table."
Solution 3: Polymorphism
Polymorphism is the cornerstone of Object-Oriented Programming. Instead of asking an object "What type are you?" and then acting on its behalf, you tell the object "Do your job."
Scenario: Employee Bonus Calculation
❌ The Bad Code
This code violates the Single Responsibility Principle. The Payroll class shouldn't handle the math for every employee type.
✅ The Good Code
Push the behavior down into the domain objects.
C#
Conclusion
Eliminating if-else chains is not just a stylistic choice; it is a structural improvement that leads to maintainable .NET applications.
- Use Interfaces (Strategy) when you need to swap entire business workflows, often combined with Dependency Injection.
- Use Delegates (Dictionary) when you have simple inputs mapping to simple actions or values, avoiding the overhead of creating multiple classes.
- Use Polymorphism when the behavior is intrinsic to the object itself (Domain-Driven Design).
By adopting these patterns, you move from imperative, procedural checks to declarative, object-oriented architecture.