[C#] Using Throw Expressions in Conditional and Null-Coalescing Operators

Before C# 7.0, throw was treated only as a “statement,” meaning it had to be written inside blocks like if statements. However, since C# 7.0, throw can be written as an “expression.”

This allows you to throw exceptions directly in places where a return value is expected, such as the right side of the conditional operator (?:) or the null-coalescing operator (??).

This feature enables you to write validation logic in constructors and property setters very concisely. This article explains the practical usage of this feature using an API client configuration class as an example.

目次

Overview of Throw Expressions

Using throw expressions allows you to write code that says, “throw an exception instead of returning a value if specific conditions are not met” in contexts where a calculation result or assignment value is required.

It is mainly used in the following situations:

  • Conditional Operator (Ternary Operator): condition ? value : throw new Exception()
  • Null-Coalescing Operator: value ?? throw new Exception()
  • Expression-Bodied Members: Right side of =>

Practical Code Example: Validating Settings

The following code is a class that manages the “Base URL” and “Timeout Settings” required for an API connection. In the constructor’s argument check, it throws exceptions directly within operators instead of using traditional if statements.

using System;

namespace ApiConfiguration
{
    public class ConnectionSettings
    {
        public string BaseUrl { get; }
        public int TimeoutSeconds { get; }

        // Constructor
        public ConnectionSettings(string baseUrl, int timeoutSeconds)
        {
            // 1. Null check using the null-coalescing operator (??)
            // If baseUrl is not null, assign it; if null, throw ArgumentNullException.
            BaseUrl = baseUrl ?? throw new ArgumentNullException(nameof(baseUrl));

            // 2. Range check using the conditional operator (?:)
            // If timeout value is positive, assign it; otherwise, throw ArgumentOutOfRangeException.
            TimeoutSeconds = (timeoutSeconds > 0)
                ? timeoutSeconds
                : throw new ArgumentOutOfRangeException(nameof(timeoutSeconds), "Timeout must be greater than 0.");
        }

        // 3. (Advanced) Usage in expression-bodied members
        // Example of throwing an exception if the property state is invalid when accessed
        private string _status;
        public string Status
        {
            get => _status;
            set => _status = value ?? throw new ArgumentNullException(nameof(value));
        }
    }

    class Program
    {
        static void Main()
        {
            try
            {
                // Normal case
                var validSettings = new ConnectionSettings("https://api.example.com", 30);
                Console.WriteLine($"Setup complete: {validSettings.BaseUrl}, {validSettings.TimeoutSeconds} sec");

                // Abnormal case 1: Specify null for URL
                // var error1 = new ConnectionSettings(null, 30);

                // Abnormal case 2: Specify invalid value for timeout
                var error2 = new ConnectionSettings("https://api.example.com", -5);
            }
            catch (ArgumentNullException ex)
            {
                Console.WriteLine($"[Null Error] {ex.Message}");
            }
            catch (ArgumentOutOfRangeException ex)
            {
                Console.WriteLine($"[Range Error] {ex.Message}");
            }
        }
    }
}

Execution Result (When running Abnormal Case 2)

[Range Error] Timeout must be greater than 0. (Parameter 'timeoutSeconds')

Technical Explanation

1. Combination with Null-Coalescing Operator (??)

Previously, checking arguments for null was typically written like this:

// Traditional method
if (baseUrl == null) throw new ArgumentNullException(nameof(baseUrl));
BaseUrl = baseUrl;

By using a throw expression, you can write this as a single assignment statement. This acts as a “Guard Clause” while reducing line count and clarifying the intent of initialization.

2. Combination with Conditional Operator (?:)

You can write throw in the second or third operand of a conditional operator.

For a conditional operator to work, both operands (the true case and the false case) must be of compatible types. Since a throw expression is compatible with any type, it is possible to have one side be a string or int and the other side be a throw expression.

3. Combination with Null-Coalescing Assignment Operator (??=) (C# 8.0+)

From C# 8.0 onwards, you can also use throw expressions on the right side of the ??= operator. This allows you to concisely write logic that assigns a value (or throws an exception) only if a property or variable is null.

// Load data if cache is null; if the load result is also null, throw an exception
cache ??= LoadData() ?? throw new InvalidOperationException("Failed to load data.");

Summary

By leveraging throw expressions, you can write validation logic—especially in constructors and property setters—concisely and declaratively. Actively consider using them in combination with null-coalescing operators and conditional operators as an effective way to improve code readability.

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

私が勉強したこと、実践したこと、してることを書いているブログです。
主に資産運用について書いていたのですが、
最近はプログラミングに興味があるので、今はそればっかりです。

目次