New C# Patterns

by CheddarCrisp

One of the reasons I like C# is that it is a constantly evolving language. There are always new patterns emerging that make things just a little easier, just a little more fun. Here's two that I like that I've learned recently.

Simpler null checks

This one is a new-ish way to do null checks. Let's say you have this model:

public class Book
{
  public string? Description { get; set; }
}

With code something like this:

Book? book = GetBookByTitle(title);

You want to do something with the description, but of course only if there is a description to work with. An older way to do this might be:

if (book?.Description != null)
{
  var description = book.Description;

  // Use the description
}

But because pattern matching works in if(), we can do this instead:

if (book?.Description is { } description)
{
  // Use the description
}

That empty { } pattern will match any value that is not null. You can combine it with a declaration all in one statement.

(Semi-)implicitly typed collection expression

If you use implicit type declarations, you probably write something like this at times:

var bookList = new List<Book> { new(...) };

Collection expressions are a nice short way of creating collections. If you want to instantiate a list using a collection expression this is one way of doing it:

List<Book> bookList = [new(...)];

But what if you really really want to keep your implicit type declarations? This won't work since the compiler has no idea what the target type should be:

var bookList = [new(...)];

If you use this, it will work again:

var bookList = (List<Book>)[new(...)];

That looks like casting, but what are we even casting? What's going on here? What's the benefit? It saves a few characters but is it worth using a construct that looks so strange and feels so wrong?

Note: This is as of .NET 9. Future behavior can obviously change.

Yes it is. At least for lists, it turns out that (List<T>)[] is faster than new List<T> { }. For nano-second scale levels of faster. Even better, if you can use (IReadOnlyCollection<T>)[] it's a little faster still.

Trust me. If you use this style long enough, it starts to look normal.