7 ways to use C# 7 throw expressions

One of the least-heralded new features in C# 7 is also one of my favorite: throw expressions. With this feature, you can now use a throw clause in a number of places where you couldn’t previously. Like many of the recent C# changes, this won’t revolutionize your coding by any means, but it will consistently make things a little bit cleaner and more concise. Here are a few of my favorite new ways to throw exceptions.

1. Ternary operators and “switch expressions”

Ternary operators are often far more concise than if-else blocks and switch statements. With C# 7, these constructs can easily throw exceptions as well:

var customerInfo = HasPermission() 
    ? ReadCustomer() 
    : throw new SecurityException("permission denied");

string timestamp = ...
var date = DateTimeOffset.TryParse(timestamp, out var dto) ? dto
    : long.TryParse(timestamp, out var unixSeconds) ? DateTimeOffset.FromUnixTimeSeconds(unixSeconds)
    : throw new FormatException($"'{timestamp}' could not be parsed as a date");

2. Null check error messaging

“Object reference not set to an instance of an object” and “Nullable object must have a value” are two of the most common errors in C# applications. With throw expressions, it’s easier to give a more detailed error message:

decimal balance = account.Balance 
    ?? throw new InvalidOperationException("account balance must be initialized");

3. Single() error messaging

Competing with null check errors for the title of most common and unhelpful error message is “Sequence contains no elements”. With the introduction of LINQ, C# programmers frequently use the Single() and First() methods to make assertions about the number of elements in a list or query. While these methods are concise, their failure provides little detail about what assertion was violated. Throw expressions provide an easy pattern for adding better error information without compromising brevity:

var customer = dbContext.Orders.Where(o => o.Address == address)
    .Select(o => o.Customer)
    .Distinct()
    .SingleOrDefault() 
    ?? throw new InvalidDataException($"Could not find an order for address '{address}'");  

4. Cast error messaging

C# 7 type patterns offer new ways to write casts. With throw expressions, we can also concisely provide specific error messages:

var sequence = arg as IEnumerable 
    ?? throw new ArgumentException("Must be a sequence type", nameof(arg));

var invariantString = arg is IConvertible c
    ? c.ToString(CultureInfo.InvariantCulture)
    : throw new ArgumentException($"Must be a {nameof(IConvertible)} type", nameof(arg));

5. Expression bodied members

Throw expressions offer the most concise way yet to implement a method with a thrown error:

class ReadStream : Stream
{
    ...
    override void Write(byte[] buffer, int offset, int count) => 
        throw new NotSupportedException("read only");
    ...
}

6. Disposal checks

Well-behaved IDisposable classes throw ObjectDisposedException on most operations after being disposed. Throw expressions can make these checks more convenient and less intrusive:

class DatabaseContext : IDisposable
{
    private SqlConnection connection;
    
    private SqlConnection Connection => this.connection 
        ?? throw new ObjectDisposedException(nameof(DatabaseContext));
    
    public T ReadById<T>(int id)
    {
        this.Connection.Open();
        
        ...
    }
    
    public void Dispose()
    {
        this.connection?.Dispose();
        this.connection = null;
    }
}

7. LINQ

LINQ provides the perfect setting to combine many of the above usages. Ever since it was released as part of C# 3, LINQ has driven C# programming towards an expression-oriented, rather than statement-oriented style. Historically, LINQ has often forced developers to make trade-offs between adding meaningful assertions and exceptions to their code and staying within the concise expression syntax that works best with lambdas. Throw expressions solve this problem!

var awardRecipients = customers.Where(c => c.ShouldReceiveAward)
    // concise inline LINQ assertion with .Select!
    .Select(
        c => c.Status == Status.None 
            ? throw new InvalidDataException($"Customer {c.Id} has no status and should not be an award recipient") 
            : c
    )
    .ToList();

Conclusion

As I’ve started to write code with C# 7, I keep finding more little ways in which throw expressions make code cleaner and more concise. Hopefully this post gives you a few ideas to try in your own code.

Mike Adelson

Mike Adelson

Software Engineer at Applied Predictive Technologies
I'm a software engineer at Applied Predictive Technologies in Washington D. C., where I work on "big data" analytics and.NET web development. In my spare time, I enjoy reading, working on various side projects, and answering questions on StackOverflow.
Mike Adelson

Latest posts by Mike Adelson (see all)

Leave a Reply