C# 14 Updates

In a few weeks, we will have the release of the new version of C# as part of .NET 10. But let's be early adopters and take a look and analyze the features now. By the way, if you want to access them, you can do so through the preview version, which is already available. However, it’s not recommended for production use.

 

 

For the sixth consecutive year, let's see what's new in the upcoming C# 14 release.

 

 

1 - Null Conditional Assignment

 

Until now, in .NET we've always had several ways to indicate that the value we want to access might be null. One option is the null-coalescing operator, which is, at the end of the day, just a if.

Person p = person ?? new Person();

 

In the previous version of C#, we saw how we could access nullable values by using the question mark, for example in the following code:

int? age = person?.Age;

The compiler does the if check for us behind the scenes to ensure the value is not null.

 

With the new change, we can apply the same logic when assigning values, by specifying the question mark ?. The compiler will generate the if statement behind the scenes, so starting with C# 14, this code:

person?.Age = 18;

Is completely valid, even if the person variable is null

 

You can read the full post I wrote about null conditional assignment here.

 

 

2 - nameof Expression Supports Unbound Generic Types

 

Another change, though a minor one, is that the nameof expression now works for generic types that do not have an associated type. 

 

We've all used nameof(Type) at some point to print a name or even the value of an enum:

string value =  nameof(List<>); 

This expression had a clear limitation, which is that it did not work with generic types without specifying the inner type, so nameof(List<>) would throw an error.

With the new version of C#, this expression is now entirely possible and can be useful for documentation, logging, error handling, etc.

 

 

3 - Field Keyword

 

With the arrival of C# 14 comes a new functionality through a new keyword: field.

This keyword lets us add logic to our getters and setters without having to manually create a backing field for them. 

 

As we know, in a class, if we want to add some logic, we must manually store the actual value in a private field, known as the backing field.

public class FieldPropExampleWithLogic
{
	
	//field
	private string _message;

	// property
	public string Message
	{
		get => _message;
		set
		{
			if (string.IsNullOrEmpty(value) || !char.IsUpper(value[0]))
			{
				throw new ArgumentException("the first letter must be uppercase");
			}
			_message = value;
		}
	}
}

 

With the new field keyword, we no longer have to do this manually – the compiler will create that backing field for us:

public class FieldKeywordExampleWithLogic
{
	public string Message
	{
		get => field;
		set
		{
			if (string.IsNullOrEmpty(value) || !char.IsUpper(value[0]))
			{
				throw new ArgumentException("the first letter must be uppercase");
			}
			field = value;
		}
	}
}

 

This is great because it lets us write less code and encapsulates the field within the getter and setter, so it cannot be modified by mistake from elsewhere. 

 

You can read more about the new field keyword here.

 

 

4 - More Support for Span and ReadOnlySpan

 

About 5 or 6 years ago, the C# team introduced a new type, Span, which allows you to quickly and safely access contiguous stack data like arrays, strings, or slices without copying them etc. (just video) – come to think of it, I don’t have a video about Span and maybe I should create one. Anyway, the thing is 

 

This new change supports implicit conversions as shown below

string[] words = ["Hello", "World"];
ReadOnlySpan<string> span = words;

 

In this example, we are converting an array to a span<T> automatically, which allows for less verbosity when programming, while minimizing the risk of causing chaos in memory. 

 

 

5 - User Defined Compound Assignment

 

Another minor change related to memory is how compound assignment works in C#.

 

Compound assignment is this type of expression: x+=y, and what actually happens behind the scenes is this: x = x+y; y When the + operator is called, a new result is produced and assigned to the x variable. 

 

With this new feature, we can directly override the +=, -=, ++ or -- operators, allowing you to include any C# logic you wish. 

 

There is another benefit: the operation can mutate the value directly instead of creating a new object.

public class E05_UserCompoundAssigment
{
    public void Test()
    {
        var x = new BigNumber(3)
        {
            Data = [1, 2, 3]
        };
        var y = new BigNumber(3)
        {
            Data = [10, 100, 1000]
        };

        x += y;
    }

    struct BigNumber
    {
        public int[] Data;

        public BigNumber(int size)
        {
            Data = new int[size];
        }

        public void operator += (BigNumber other)
        {
            for (int i = 0; i < Data.Length; i++)
                Data[i] += other.Data[i];
        }
    }
}

 

This is a bit of a niche use case, but I am sure that anyone working with large sets of data that need to operate on each other will see improvement by reducing the number of assignments in memory. 

 

 

6 - Extension Members Everywhere

 

In C#, there have long been extension methods that allow you to “add” methods to existing types by declaring them as static methods in a static class, using this on the first parameter. 

 

With the proposal for extension members in C# 14, a new syntax with the extension keyword allows you to extend a type not just with methods, but also with properties, indexers, and more, making these extensions appear as "normal" members of the original type.

public static class MyExtensions
{
    extension(IEnumerable<int> source)
    {
        public IEnumerable<int> WhereGreaterThan(int threshold)
            => source.Where(x => x > threshold);

        public bool IsEmpty
            => !source.Any();
    }
}

 

This change doesn't mean you have to change your code, but personally, I see value in starting to use it for new code.

 

You can see the complete analysis here.

 

 

 

7 - Features Not Present in C# 14

 

As every year, I want to mention those features that are not ready yet for one reason or another and that the community is desperately asking for

 

7.1 - Type Unions / Discriminated Unions

 

We have been asking for union types for years and last year there was already a proposal to include them, but it’s more complex than it seems at first sight. In one talk, they already said it would likely be postponed until C#16 or C#17, so let’s see if next year we get a preview, which would mean it’d almost definitely be ready for C# 16, or we’ll have to wait longer.

 

You can check out the proposal and how it will work on GitHub.  

 

And with this proposal, maybe the result pattern could be implemented natively in C#

 

7.2 - Native Eventing Framework in C#

 

For quite some time, there was a proposal to implement an event framework within .NET to process messages using different providers. This proposal was intended to be released in .NET9. 

 

Imagine any of the cloud providers like EventBridge, SQS, EventBus, Kafka, RabbitMQ, etc.

 

 

Personally, I think it’s a great idea because in every company I go to, they have their own abstraction over the same problem, but each implementation works a bit differently. In C#, we already solved this for logging, where you can use the Ilogger<T> interface with any implementation you want.

 

 

Unfortunately, due to community feedback , mainly from the authors of popular libraries in this area , it was decided to cancel the proposal in order to “avoid interruptions or overlaps in the ecosystem,” which I find, frankly, a bit ridiculous, since over the last few years, they have released several features that literally killed some libraries.

 

I don't know the real reason, but it’s clear that unless it’s related to artificial intelligence or Aspire, there’s not much interest in investing time in it. 

 

You can read the proposal on GitHub here.

 

This post was translated from Spanish. You can see the original one here.
If there is any problem you can add a comment bellow or contact me in the website's contact form

© copyright 2025 NetMentor | Todos los derechos reservados | RSS Feed

Buy me a coffee Invitame a un café