2025/12/19 / Unity

C#14:New Features Explained

An easy-to-understand, example-driven explanation of notable features introduced in C# 14.

unity csharp

Hello! I'm Pan-kun.

This time I'd like to pick up and introduce several features from the C# 14 documentation that was published at the end of last month.
Official reference:
What's new in C# 14learn.microsoft.comWhat's new in C# 14Get an overview of the new features in C# 14. C# 14 ships with .NET 10.

C# 14 — New Features

The field keyword

Many developers commonly use fields and properties in patterns like the following. The main purpose, as you know, is encapsulation. With this new feature, you can now write:

Csharp
// before
private string m_foo;
public string Foo
{
    get => m_foo;
    private set => m_foo = value; 
    // If you wanted to add a null check:
    // ?? throw new ArgumentNullException(nameof(value))
}

// after
public string Foo
{
    get;
    set => field = value;
} 

For developers using an IDE with IntelliSense, many type m_ and rely on completion, so whether this is helpful depends on coding style. If you group members with regions or care about explicit naming for readability, you might prefer the traditional pattern. For teams with well-defined coding conventions, choose whichever fits your workflow.

The docs also explain how to handle naming conflicts with existing symbols named field. However, if you find a symbol actually named field in a codebase, consider running git blame to see who added it and proceed cautiously.

Reading code of a type that contains a symbol named field can cause breaking changes or confusion.
You can disambiguate between the field keyword and an identifier by using @field or this.field, or rename the current field symbol to make distinctions clearer.

What's new in C# 14learn.microsoft.comWhat's new in C# 14Get an overview of the new features in C# 14. C# 14 ships with .NET 10.

Extension members

This is the feature related to the familiar this ClassName valueName extension-method parameter syntax.

Csharp
public class Foo 
{
    public int hoge = 1;
}

// before
public static class FooExtensions
{
    public static int BeforeExtension(this Foo foo) => foo.hoge;
}

// after
public static class FooExtensions
{
    extension(Foo foo)
    {
        public int AfterExtension() => foo.hoge;
    }
}

Benefits:

  • You no longer need to explicitly declare the instance parameter for each extension member.
  • Placing members inside an extension block makes the code visually clearer.
  • You can declare extension properties.
  • You can also declare extension operators.

For more details, see the official docs:
What's new in C# 14learn.microsoft.comWhat's new in C# 14Get an overview of the new features in C# 14. C# 14 ships with .NET 10.

Null-conditional assignment

Csharp
public class Hoge
{
    public string? Name { get; set; } = null;
}

string GetName() => "Pankun";

void Foo(Hoge? hoge)
{
    // before
    if (hoge is not null) // Null check can be simplified (IDE0031)
        hoge.Name = GetName();
      
    // after  
    hoge?.Name = GetName();  
}

Benefits:

  • Reduces nesting.
  • Improves readability.

Partial members

Csharp
// Foo.cs
public partial class Foo
{
    // constructor implementation
    public partial Foo()
    {
        Hoge();
    }
    
    // add member implementation
    private partial void Hoge() => Console.WriteLine("Partial Hoge");
}

// Foo.members.cs
public partial class Foo
{
    // You can add declarations here:
    // constructor declaration
    // static is not supported
    public partial Foo(); // Success
    private partial void Hoge(); // Success
}

This helps avoid the "How is this call valid when there's no definition here?" confusion.

nameof support for unbound generic types

Csharp
public static void ShowExample()
{
    // Before
    Console.WriteLine(nameof(List<int>)); // List<int>
    
    // After
    // The argument to nameof can be an unbound generic type.
    Console.WriteLine(nameof(List<>)); // List
    // NOTE: Use unbound generic type (IDE0340)
    Console.WriteLine(nameof(List<int>)); // List<int> 
}

This allows broader kinds of name-based checks. (I also learned that a generic type without its type arguments specified is called an "unbound generic type".)

.NET 10

There are other features added in C# 14, but .NET also received several improvements recently, so I'll briefly touch on one of them.

For details, see:
What&#39;s new in .NET 10learn.microsoft.comWhat&#39;s new in .NET 10Learn about the new features introduced in .NET 10 for the runtime, libraries, and SDK. Also find links to what&#39;s new in other areas, such as ASP.NET Core.

Shebang

You can now add a shebang to run a file as a script. This eliminates the need to create a project or write a Main method for quick scripts.

Csharp
#!/usr/bin/env -S dotnet run
Console.WriteLine("Hoge");

I also heard there were improvements in Blazor.

Conclusion

Although still in preview, these features seem quite promising for private and experimental coding. I expect to use many of them frequently — give them a try!

← Recommended Z…← Back to BlogAdd Syntax Hi… →