Skip to main content

Refactoring Traditional Statements into Expressions in C#

When programming in C#, many developers use traditional statements like if-statements, switch-statements, for-loops, and foreach-loops. While these are powerful tools, expressions and other features in recent C# versions offers more concise and readable alternatives. In this article, I'll share a few tips on how you can refactor these traditional statements into sleeker expressions.

Replacing if-statements and switch-statements with Expressions

Traditional if-statements are useful but can sometimes lead to verbose code, especially for simple evaluations. By transitioning to expression-bodied members or conditional operators, we can often reduce the lines of code and enhance readability.

Using Conditional Operator

Instead of a traditional if-else statement, the conditional (ternary) operator can be used.

The conditional ternary operator in C# is a concise way to evaluate one of two expressions based on a boolean condition. It's a shorthand for the if-else statement and is denoted by the ? and : symbols.

Example:


int value = 10;
string result = (value > 5) ? "Greater than 5" : "5 or less";

In the above example, since the condition value > 5 is true, the result will be set to "Greater than 5". If the value was 5 or less, the result would be set to "5 or less".

Refactoring

Let's use a bit more complex example where I first write the traditional if-statement, directly followed by a refactored example using conditional operator expression.

var result =" ";
if(isValid){
    if(theValue < 0){
        result = "Negative";
    }else if(theValue < 500){
        result = "Small Positive";
    }else{
        result = "High Positive";
    }
}else{
    result =   "Not Valid";
}

// Can be refactored to more condensed code
var result = isValid ? 
     theValue < 0  ?  
          "Negative" : theValue < 500 ? 
               "Small Positive" : "HighPositive" : 
    "Not Valid";

Using Null Coalescing operator

The null coalescing expression in C# allows you to provide a default value for nullable value types or reference types. It returns the left-hand operand if it's not null; otherwise, it returns the right-hand operand. The ?? operator is used to represent this expression.

Example

string name = null;
string displayName = name ?? "Guest";

In this example, since name is null, the displayName will be set to "Guest". If name had a non-null value, displayName would take on that value.

Refactoring

You have probably seen code similar to the example below. A couple of API calls returning a value. The value from first call is used as input to the second call. If the value is null, then an exception is thrown.

var resultFromFirstCall = await myApi.PostEntityAsync(requestEntity);
if (resultFromFirstCall == null){
    throw new ArgumentNullException("PostEntity Result");
}

var resultFromSecondCall = await myApi.PostResultEntityAsync(resultFromFirstCall);
if (resultFromSecondCall == null){
    throw new ArgumentNullException("Result Entity");
}

var resultFromThirdCall = await myApi.PostResultEntityAsync(resultFromSecondCall);
if (resultFromThirdCall == null){
    throw new ArgumentNullException("Third Result Entity");
}

The code above could be refactored by using the null coalescing operator instead, which in my opinion makes the code a lot less cluttered and more readable:

var resultFromFirstCall = await myApi.PostEntityAsync(requestEntity) ?? throw new ArgumentNullException("PostEntity Result");

var resultFromSecondCall = await myApi.PostResultEntityAsync(resultFromFirstCall) ?? throw new ArgumentNullException("Result Entity");

var resultFromThirdCall = await myApi.PostResultEntityAsync(resultFromSecondCall) ?? throw new ArgumentNullException("Third Result Entity");

Switch Expressions

C# 8 introduced switch expressions, offering a more concise syntax compared to traditional switch-statements.

string dayOfWeek = day switch
{
    0 => "Sunday",
    1 => "Monday",
    // ... other cases
    _ => "Invalid Day"  // Default case
};

Using switch expressions like the above not only reduces boilerplate but also returns a value directly.

Transforming Loops to Expressions

Loops are fundamental in C#. However, LINQ (Language Integrated Query) provides expressive methods to perform common operations which would have required loops previously.

Replacing foreach with LINQ

The following example shows how a foreach loop can be replaced with a LINQ query.

List<string> names = new List<string>() { "Alice", "Bob", "Charlie" };
List<string> uppercaseNames = new List<string>();

foreach (var name in names)
{
    uppercaseNames.Add(name.ToUpper());
}
// can be replaced by:
List<string> uppercaseNames = names.Select(name => name.ToUpper()).ToList();

Using LINQ for Looping

For loops, especially those that iterate over a range of numbers, can be expressed using LINQ's Enumerable.Range.

for (int i = 1; i <= 10; i++)
{
    Console.WriteLine(i);
}
// can be replaced by:
Enumerable.Range(1, 10).ToList().ForEach(Console.WriteLine);

Why Use Expressions Over Traditional Statements?

  • Readability: Expressions, especially with LINQ, are often more concise, making code easier to read and understand.
  • Maintenance: Reduced lines of code mean fewer places for bugs to hide and less code to maintain.
  • Expressiveness: Using expressions can convey intentions more directly, especially with operations like filtering, mapping, and reducing.

In conclusion, while traditional if-statements, switch-statements, and loops have their place, the power of C# expressions allows for cleaner, more readable, and maintainable code. Embracing these patterns can elevate your coding style and improve the overall quality of your applications.

csharp, functional-programming, linq, expressions, refactoring, .net

  • Hits: 568