C#14:New Features Explained
An easy-to-understand, example-driven explanation of notable features introduced in C# 14.
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:
learn.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:
// 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
fieldcan cause breaking changes or confusion.
You can disambiguate between thefieldkeyword and an identifier by using@fieldorthis.field, or rename the currentfieldsymbol to make distinctions clearer.
Extension members
This is the feature related to the familiar this ClassName valueName extension-method parameter syntax.
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
extensionblock makes the code visually clearer. - You can declare extension properties.
- You can also declare extension operators.
For more details, see the official docs:
learn.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
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
// 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
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.
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.
#!/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!

Loading comments...