Skip to main content
What's New in C# 14: A Developer's Guide

What's New in C# 14: A Developer's Guide

A deep dive into C# 14 features including the field keyword, extension properties, and implicit span conversions. How these changes impact your daily .NET development.

  1. Posts/

What's New in C# 14: A Developer's Guide

·884 words·5 mins· loading
👤

Chris Malpass

Author

It’s that time of year again. With the release of .NET 10, we also get our hands on C# 14.

As a language, C# has been on a steady trajectory of reducing boilerplate and improving expressiveness. If C# 12 and 13 were about laying the groundwork for performance and collection literals, C# 14 feels like a “Quality of Life” release. It tackles some of the longest-standing requests from the community (looking at you, field keyword) and smooths out rough edges in the type system.

Let’s dive into the features that will actually change how you write code day-to-day.

The field Keyword: Finally!
#

If you’ve written C# for any length of time, you’ve written this pattern a thousand times. You need a property with just a little bit of logic in the setter—maybe validation, maybe raising an INotifyPropertyChanged event. Suddenly, your concise auto-property explodes into a verbose backing field ritual.

Before C# 14:

1
2
3
4
5
6
7
8
9
private string _name;
public string Name
{
    get => _name;
    set
    {
        _name = value ?? throw new ArgumentNullException(nameof(value));
    }
}

We all hated writing _name. It polluted the class scope and felt like unnecessary noise.

With C# 14:

1
2
3
4
5
public string Name
{
    get;
    set => field = value ?? throw new ArgumentNullException(nameof(value));
}

The field keyword gives us access to the compiler-synthesized backing field. It’s cleaner, it keeps the scope contained to the property, and it removes the need for manual field declarations.

Warning: If you have existing variables named field in your scope, you might hit a breaking change. You can use @field or this.field to disambiguate, but honestly, it’s probably better to just rename your variable.

Extension Members: Beyond Methods
#

Extension methods have been a staple of C# since LINQ (C# 3.0). They allowed us to add methods to types we didn’t own. But they had limits: you couldn’t add properties, and you couldn’t add static members.

C# 14 introduces a new extension syntax that blows these limitations wide open.

Extension Properties
#

You can now add properties to existing types. This is fantastic for adding computed helpers to third-party classes.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
public static class EnumerableExtensions
{
    // New 'extension' block syntax
    extension<T>(IEnumerable<T> source)
    {
        // Look ma, an extension property!
        public bool IsEmpty => !source.Any();
    }
}

// Usage
var list = new List<int>();
if (list.IsEmpty) { ... }

Static Extension Members
#

This is the game changer. You can now add members that appear to be static members of the type itself.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
public static class EnumerableExtensions
{
    // Extension block with receiver type only (no parameter name)
    extension<T>(IEnumerable<T>)
    {
        public static IEnumerable<T> Identity => Enumerable.Empty<T>();
        
        // Even operators!
        public static IEnumerable<T> operator +(IEnumerable<T> left, IEnumerable<T> right) 
            => left.Concat(right);
    }
}

// Usage
var combined = listA + listB; // Using the extension operator

This feature allows library authors to provide a much more “native” feel to their helper libraries.

Implicit Span Conversions
#

Span<T> and ReadOnlySpan<T> are the backbone of modern high-performance .NET. However, using them often required explicit casting or verbose calls, which added friction to adoption.

C# 14 introduces first-class support for implicit conversions.

1
2
3
4
5
6
7
8
9
void ProcessText(ReadOnlySpan<char> text) { ... }

string myString = "Hello World";
char[] myChars = ['H', 'i'];

// Before: often needed explicit handling or overloads
// C# 14: Just works
ProcessText(myString); 
ProcessText(myChars);

This might seem minor, but it removes the API friction that often discouraged developers from using Span in public APIs. It encourages “performance by default.”

Null-Conditional Assignment
#

We’ve all written code like this:

1
2
3
4
if (customer != null)
{
    customer.Order = new Order();
}

C# 14 allows us to use the null-conditional operator ?. on the left-hand side of an assignment.

1
customer?.Order = new Order();

If customer is null, the assignment simply doesn’t happen. It also works with compound assignments (like +=), though notably not with increment/decrement operators (++/--).

1
2
// Only adds 10 if Account is not null
user?.Account.Balance += 10; 

Quick Hits
#

There are several other improvements worth noting:

  • nameof(List<>): You can finally use nameof on unbound generic types. No more nameof(List<int>) just to get the string “List”.
  • Lambda Modifiers: You can now use modifiers like ref, out, and scoped on lambda parameters without specifying explicit types.
    1
    
    var parser = (string s, out int result) => int.TryParse(s, out result);
    
  • Partial Constructors & Events: Expanded support for partial members allows source generators to hook into object construction and event handling more effectively. This is a boon for frameworks like MAUI and Blazor that rely heavily on generated code.

Final Thoughts
#

C# 14 is a pragmatic release. It doesn’t try to reinvent the paradigm; instead, it looks at how we actually write code and removes the friction. The field keyword alone is going to delete thousands of lines of boilerplate across the ecosystem.

As we move to .NET 10, these features allow us to write code that is both more concise and more performant.

For more details, check out the official C# 14 announcement and the What’s New documentation.