Definition of Cyclomatic Complexity in C#
1 2 3 4 5 |
if while for foreach case default continue goto && || catch ternary operator ?: ?? and or |
The following expressions are not counted for CC computation:
1 2 3 4 5 |
else do switch try using throw finally return object creation method call field access |
Example of Cyclomatic Complexity Impact in C#
Exhibiting a Complex Method
if
and else
scopes. The keyword if
is used six times and &&
is used once. Hence its Cyclomatic Complexity score is 8:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
public static class OrderLogic { public static void ProcessOrder( int orderId, bool isPriority, bool isInternational, bool isGift, bool isCouponApplied, decimal orderTotal) { if (orderId <= 0) { Console.WriteLine("Invalid order ID."); return; } if (isPriority) { Console.WriteLine("Processing priority order."); if (isInternational) { Console.WriteLine("Processing international priority order."); if (isGift) { Console.WriteLine("This is a gift order."); } } } else { Console.WriteLine("Processing standard order."); if (isInternational) { Console.WriteLine("Processing international standard order."); } } if (isCouponApplied && orderTotal > 100) { Console.WriteLine("Applying discount for orders over $100."); } else { Console.WriteLine("No discount applicable."); } } } |
Refactoring the Complex Method in Several Simpler Methods
The method above can be refactored into several less complex methods. In the code we use CC to refer to each method Cyclomatic Complexity score:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
public static class OrderLogic { public static void ProcessOrder( int orderId, bool isPriority, bool isInternational, bool isGift, bool isCouponApplied, decimal orderTotal) { // CC 2 if (!IsValidOrder(orderId)) return; ProcessOrderType(isPriority, isInternational, isGift); ApplyDiscountIfEligible(isCouponApplied, orderTotal); } private static bool IsValidOrder(int orderId) { // CC 2 if (orderId <= 0) { Console.WriteLine("Invalid order ID."); return false; } return true; } private static void ProcessOrderType( bool isPriority, bool isInternational, bool isGift) { // CC 5 if (isPriority) { Console.WriteLine("Processing priority order."); if (isInternational) { Console.WriteLine("Processing international priority order."); } } else { Console.WriteLine("Processing standard order."); if (isInternational) { Console.WriteLine("Processing international standard order."); } } if (isGift) { Console.WriteLine("This is a gift order."); } } private static void ApplyDiscountIfEligible( // CC 3 bool isCouponApplied, decimal orderTotal) { if (isCouponApplied && orderTotal > 100) { Console.WriteLine("Applying discount for orders over $100."); } else { Console.WriteLine("No discount applicable."); } } } |
Benefits of Refactoring:
- Simpler Control Flow: The main method now delegates specific tasks to smaller, more focused methods.
- Easier to Test: You can test each smaller method independently.
- Lower Cyclomatic Complexity: The complexity is spread across multiple methods, making each method easier to understand and maintain independantly.
Measuring Cyclomatic Complexity in C#
Ruling C# Cyclomatic Complexity
C# Cyclomatic Complexity and Tests
Writing tests for your code is nowadays an essential practice for every professional C# developer. Typically, a test covers a single execution path, while the Cyclomatic Complexity score of a method represents the number of independent execution paths. Therefore, Cyclomatic Complexity provides a rough estimate of how many tests are required to fully test a method.
By running tests, you can determine the code coverage for each method. A method partially covered means that not all its independent execution paths are challenged by tests. The rule Methods should have a low C.R.A.P score spots methods that both have high Cyclomatic Complexity scores and are poorly tested (C.R.A.P stands for Change Risk Analyzer and Predictor). The matched methods clearly indicate pain points in your code and should be tested and refactored.