Skip to content

Leveraging Expressions in C#

Published: at 03:55 PM

In C#, developers often default to using statements, especially when implementing common patterns like singletons. However, by favoring expressions over statements, we can write more concise, readable, and maintainable code. In this article, we will explore how to refactor a typical singleton pattern by using expression-based syntax, including operators and expression-bodied members.

Using Operators Instead of Statements

Let’s start with a basic example of a singleton class:

public sealed class MySingleton
{
    private static MySingleton _instance;

    private MySingleton()
    {
    }

    public static MySingleton Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = new MySingleton();
            }
            return _instance;
        }
    }

    // Other code here
}

In the Instance property, we use an if statement to check if the _instance is null. If it is, we create a new instance. Otherwise, we return the existing one. This approach works fine, but it’s a bit verbose. We can simplify this logic by using the ternary operator, which allows us to condense the code into a single line:

public sealed class MySingleton
{
    private static MySingleton _instance;

    private MySingleton()
    {
    }

    public static MySingleton Instance
    {
        get
        {
            return _instance == null ? new MySingleton() : _instance;
        }
    }

    // Other code here
}

While this is an improvement, it still has some room for refinement. Specifically, the null check and the conditional instance creation can be expressed even more succinctly using the null-coalescing operator (??).

Introducing the Null-Coalescing Operator

The null-coalescing operator simplifies the process of returning the existing instance or creating a new one if it’s null:

public sealed class MySingleton
{
    private static MySingleton _instance;

    private MySingleton()
    {
    }

    public static MySingleton Instance
    {
        get
        {
            return _instance ?? (_instance = new MySingleton());
        }
    }

    // Other code here
}

This version is cleaner and more compact. By using the ?? operator, we eliminate the need for an explicit null check and the verbose if-else structure. The logic remains the same: if _instance is null, we create a new MySingleton and assign it to _instance. Otherwise, we return the existing instance.

Expression-Bodied Members

C# provides another feature that can further simplify this code: expression-bodied members. Expression-bodied members allow us to replace a method or property body with a more concise expression, using a lambda-like syntax (=>). This is particularly useful for simple getters or methods that return a single expression.

Let’s refactor the singleton pattern using an expression-bodied member:

public sealed class MySingleton
{
    private static MySingleton _instance;

    private MySingleton()
    {
    }

    public static MySingleton Instance => _instance ?? (_instance = new MySingleton());

    // Other code here
}

In this version, the Instance property is defined using the => syntax. The compiler automatically generates the getter, and the property is now a single, concise expression. This not only reduces boilerplate code but also makes the logic immediately clear to anyone reading the code.

Conclusion

Favoring expressions over statements in C# can lead to cleaner, more maintainable code. By using operators like the null-coalescing operator and taking advantage of expression-bodied members, we can simplify common patterns, making our code more predictable and easier to test.

In summary, while C# is a statement-based language, it’s beneficial to embrace expressions where possible. This approach not only reduces the amount of code but also enhances readability, making it easier for other developers (or even your future self) to understand and maintain the codebase.