Here's what's new in C# 14

 

Overview

With improvements to developer productivity, code clarity, and performance, C# 14, released alongside .NET 10, continues Microsoft's tradition of refining the language. In this article, you will find information about syntax enhancements, performance optimizations, and new language capabilities in C# 14, along with practical code examples.

The field Keyword

In C# 14, the field keyword simplifies property declarations by allowing property accessors to interact with their automatically generated backing field without explicitly defining it.

Before C# 14

Traditional Property with Backing Field This approach involves defining a private field within a class to store the property's value and using the property itself to control access to that field. By doing so, the property's value can be encapsulated and better controllable.

namespace CSharpe14.Example.Helpers;
public class Extensions
{
    private  string _message;

    //Before C#14
    public string MessageBeforeCSharpe14
    {
        get => _message;
        set => _message = value ?? throw new ArgumentNullException(nameof(value));
    }
}

In C# 14

C# 14 uses fields to make cleaner code by declaring them at the top of the class and initializing them in the constructor. By keeping related code together, this approach improves readability and maintainability. Additionally, it simplifies debugging and testing by reducing the need to look for field assignments throughout the code.

namespace CSharpe14.Example.Helpers;
public class Extensions
{
    private  string _message;
    //After C#14
    public string MessageCSharpe14
    {
        get;
        set => _message = value ?? throw new ArgumentNullException(nameof(value));
    }

}

As a result, property declarations are more concise and maintainable thanks to the implicit backing field.

Constraints for Span<T> and ReadOnlySpan<T>

C# 14 introduces implicit conversions for Span<T> and ReadOnlySpan<T>, improving performance by removing unnecessary allocations. For handling large data sets efficiently, this is especially useful. With this feature, developers can work more seamlessly with these types, reducing the need to perform explicit conversions and improving code readability. Thus, applications requiring intensive data processing may be able to achieve significant performance gains.

Implicit Conversion from Array to ReadOnlySpan<T>

namespace CSharpe14.Example.Helpers;

public class Extensions
{
   public static void ProcessData(ReadOnlySpan<byte> data)
    {
        Console.WriteLine("C#14 Processing Data:");
        foreach (var item in data)
        {
            Console.Write($"{item} ");
        }
        Console.WriteLine();
    }

}
 try
{
    byte[] byteArray = { 1, 2, 3, 4, 5 };


    ProcessData(byteArray);
    ProcessData(byteArray.AsSpan(1, 3));
    ReadOnlySpan<byte> spanData = stackalloc byte[] { 50, 60, 10 };
    ProcessData(spanData);
}
catch (global::System.Exception ex)
{
    Console.Writeline($"C#14 errors are as {ex} and {message}");

}

Due to this feature, no copies of memory are created, thereby reducing memory pressure. Applications that handle large datasets or perform memory-intensive operations can significantly benefit from increased memory utilization and improved performance as a result.

Modifiers on Lambda Parameters (ref, in, out)

In C# 14, lambda expressions can be modified directly with parameter modifiers (ref, in, out), improving performance and flexibility. In addition to improving code execution, C# 14 allows for greater control over variable management, making it an effective tool for developers seeking optimized and adaptable programming solutions. In addition to streamlining coding practices, this feature improves application performance as well. As a result of C# 14's improved variable management, code is both cleaner and more efficient, leading to faster execution times and reduced memory usage.

ref Parameters in Lambda Expressions

namespace CSharpe14.Example.Helpers;

public class Extensions
{
    public static void GetRandom(out int value) => value = new Random().Next(1, 100);   
}
try
{
    int randomNumber;
    GetRandom(out randomNumber);
    Console.WriteLine($"Random Number: {randomNumber}");
}
catch (global::System.Exception)
{
    Console.Writeline("C#14 errors");
}

It was previously possible to use modifiers such as ref only in method signatures, but now they can be used in lambdas, reducing boilerplate. In addition to simplifying code and improving readability, functional programming becomes more concise and expressive as a result of this enhancement.

 

Enhanced nameof Operator for Generic Types

The nameof operator now supports unbound generic types, allowing developers to get the name of a generic type without specifying its type parameters. Developers can now spend more time on logic and less time on syntax, leading to cleaner, more maintainable code, especially when dealing with complex generic types.

Getting the Name of a Generic Class

try
{

    string genericClassName = nameof(SampleClass<>);
    Console.WriteLine($"Generic class name in C#14 example: {genericClassName}"); 

    Type closedGenericType = typeof(SampleClass<int>);
    Console.WriteLine($"Closed generic type in C#14 is as example: {closedGenericType.Name}"); 

    string cleanName = closedGenericType.Name.Split('`')[0];
    Console.WriteLine($"Clean generic class in C#14 example is a name: {cleanName}"); 
}
catch (global::System.Exception)
{

    Console.WriteLine($"C#14 has a error occurred: {ex.Message}");
}

Code introspection and maintainability are improved, which leads to easier debugging and faster updates, ensuring that the codebase remains robust and adaptable over time. By providing clear, understandable code structures, it also facilitates better collaboration among developers.

File-Scoped using Declarations

Using declarations in C# 14 can be file-scoped, reducing clutter in code files and limiting the scope of imported namespaces. This enhancement simplifies dependency management and improves code readability by minimising the need for repetitive statements in methods and classes. By doing so, developers can focus on logic rather than housekeeping.

Using a Namespace at the File Level

namespace CSharpe14.Example.Models;
public record Person(string Name, int Age);
 try
{
    var person = new Person("Lerzan Ali", 42);

    string json = JsonSerializer.Serialize(person);
    Console.WriteLine($"Serialized JSON Example in C#14: {json}");
    
    Person? deserializedPerson = JsonSerializer.Deserialize<Person>(json);
    Console.WriteLine($"Deserialized Person Example in C#14: {deserializedPerson?.Name}, {deserializedPerson?.Age}");
        
    Console.WriteLine(person); 
}
catch (global::System.Exception)
{

    Console.WriteLine($"C#14 has a error occurred: {ex.Message}");
}

By using this feature, you will be able to improve the organization and maintainability of your code.

Improved params in Methods

C# 14 now supports params parameters with Span<T>, improving method performance and reducing allocations. This enhancement allows developers to write more efficient code by minimizing memory usage and boosting execution speed, especially in scenarios requiring frequent method calls with varying numbers of arguments. In turn, applications can perform and scale better.

Using params with Span<T>

public static void PrintNumbers(Span<int> numbers) => numbers.ToArray().ToList().ForEach(Console.WriteLine); 
try
{
    int[] array = { 1, 2, 3, 4 };
    PrintNumbers(array);
}
catch (global::System.Exception)
{

    Console.WriteLine($"C#14 has a error occurred: {ex.Message}");
}

Through this enhancement, unnecessary array allocations are eliminated, resulting in improved performance. By optimizing memory usage, the system is able to handle larger datasets and more complex computations without slowing down. As a result, execution times are shortened and overall operation is more efficient.

Summary

The C# 14 language has a number of powerful features that enhance code clarity, enhance performance, and streamline development. The following are some of the key takeaways:

  • Property accessors are simplified with the field keyword
  • The implicit conversions for Span<T> optimize memory usage
  • Ref, in, and out parameters are now supported in lambda expressions
  • Unbound generic types can be used with the nameof operator
  • Code organization is improved by file-scoped using declarations
  • params with Span<T> enhances performance

 

You can download .Net 10 Preview on the following Link: https://dotnet.microsoft.com/en-us/download/dotnet/10.0, and the code examples in this article are shared on my GitHub Repository: Repository: https://github.com/ziggyrafiq/CSharp14Features

Got thoughts on C# 14? Share them in the comments below!

Happy coding!

Up Next
    Ebook Download
    View all
    Learn
    View all
    Capgemini is a global leader in consulting, technology services, and digital transformation.