C# 14 Features

In this post, we will explore the upcoming features in C# 14 (.NET 10). You can try these features using .NET 10, currently in preview 3, and Visual Studio preview 17.14 or higher.

In this post, we will explore the following features.

  1. field Keyword
  2. Extension Members
  3. Null Conditional assignment

field Keyword

In previous versions of .NET, we were family with auto properties.

public int Foo { get; set; }

.NET traditionally always had the field-backed properties, which allowed developers to add more logic to the property accessors when required.

private int _foo;
public int Foo
{
    get
    {
        // Do something
        return _foo;
    }
    set
    {
        // Additional logic
        _foo = value;
        // Some more additional logic
    }
}

But this approach had the extra step of creating the backing field.

With .NET 14, the field keyword allows developers to write a property accessor without explicitly declaring a backing field.

public int Foo
{
    get
    {
        // Do something
        return field;
    }
    set
    {
        // Additional logic
        field = value;
        // Some more additional logic
    }
}

Now we have the best of both worlds, the properties are more concise without the need for creating an explicit backing field. The compiler replaces the field token with compiler-generated backing fields in the runtime.

[CompilerGenerated]
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private int <Foo>k__BackingField;

public int Foo
{
    get
    {
        return <Foo>k__BackingField;
    }
    set
    {
        <Foo>k__BackingField = value;
    }
}

Extension Members

Extension methods have been part of .NET for some time now. But what we lacked was a way to add an extension property. The current syntax has a sort of limitation in the ability to add extension properties and this could be the reason why the language developers decided to introduce a new syntax extension for members.

Previously, we could declare extension methods using the following format.

internal sealed class SpecialList<T>
{
    public List<T> Items { get; private set; } = new List<T>();

    public void Add(T item)
    {
        Items.Add(item);
    }
}
internal static class ExtensionMembers
{
    public static void InsertOne<T>(this SpecialList<T> source, int index, T item)
    {
        if (index < 0 || index > source.Items.Count)
        {
            throw new ArgumentOutOfRangeException(nameof(index), "Index is out of range.");
        }
        source.Items.Insert(index, item);
    }
}

In C# 14, we could define extension methods using the following syntax.

internal static class ExtensionMembers
{
    public static void Insert<T>(this SpecialList<T> source, int index, T item)
    {
        if (index < 0 || index > source.Items.Count)
        {
            throw new ArgumentOutOfRangeException(nameof(index), "Index is out of range.");
        }
        source.Items.Insert(index, item);
    }
}

The newly introduced extension blocks allow developers to define extension methods are properties. You no longer needn't supply the type instance as the first parameter of the extension method. This is done in the extension block. The new syntax allows for defining extension properties as well.

internal static class ExtensionMembers
{
    public static int Count<T>(this SpecialList<T> source)
    {
        return source.Items.Count;
    }
}

What is more, you can also define static extension members.

internal static class ExtensionMembers
{
    extension<T>(SpecialList<T>)
    {
        public static bool IsEmpty(IEnumerable<T> source) => !source.Any();
    }

}

You can now invoke the IsEmpty method on the type Specialist<T>, instead of the instance. For example, SpecialList<int>.IsEmpty([1,2,3]).

Example usages

var specialList = new SpecialList<int>();
specialList.Add(1);
specialList.Add(2);
specialList.Add(4);

// Extension members
specialList.Insert(2, 3);
specialList.InsertOne(2, 3);

foreach (var items in specialList.Items)
{
    Console.WriteLine(items);
}

Console.WriteLine($"Count: {specialList.Count}");
Console.WriteLine($"IsEmpty: {SpecialList<int>.IsEmpty(new int[] { 1, 2, 3 })}");

/* Output
1
2
3
3
4
Count: 5
IsEmpty: False
*/

Null-Conditional Assignment

The last feature we will explore in this blog post is the Null conditional assignment. As .NET C# developers, we are all aware of the Null Conditional Operator.

foo?.Demo();

In the above code, foo. The Demo() method is only called if the foo is not null. But what has been missing so far is the ability to assign a property conditionally. In previous versions of .NET, we would have to check if the variable is null before assigning a property to it. For example,

if (foo is not null)
{
    foo.Bar = 20;
}

This is what is being solved in C# 14. We can conditionally assign properties. For example

foo?.Bar = 20;

The right side of the operator = is evaluated only when the left side is not null. We can also use this feature with compound assignment operators such as += and -=;

Conclusion

In this blog post, we explored 3 new features in C# 14. We will continue this journey and explore more features in future posts.