C# continues to be a dominant language in the development world, thanks to its versatility and the frequent updates it receives. With the release of C# 10 as part of .NET 6, Microsoft has introduced several new features designed to enhance productivity, simplify code, and improve performance. Whether you’re a seasoned developer or someone just starting out, learning these new features can take your C# skills to the next level.
This guide will walk you through the major new features of C# 10, explain their benefits, and show you how to use them effectively in your projects.
Table of Contents
- Introduction to C# 10
- Global Using Directives
- File-Scoped Namespaces
- Improvements to Record Types
- Struct Improvements with
with
Expression - Improved Lambda Expressions
- Constant Interpolated Strings
- Nullable Reference Types Enhancements
- Extended Property Patterns
- Performance and Miscellaneous Enhancements
- Conclusion
1. Introduction to C# 10
C# 10 was released as part of the .NET 6 wave, which introduced a range of improvements to the C# language and the .NET ecosystem. This version is focused on making C# even more developer-friendly by simplifying the syntax and removing common pain points in large codebases. Many of the changes in C# 10 are aimed at cleaner code, improved performance, and enhanced productivity.
Some of the most notable features in C# 10 include:
- Global using directives to eliminate redundant
using
statements. - File-scoped namespaces to simplify namespace declarations.
- Enhancements to record types and structs.
- Improvements to lambda expressions and property patterns.
Let’s dive into each of these features and see how they can help you write cleaner, more maintainable code.
2. Global Using Directives
One of the most common complaints in C# projects, especially large ones, is having to add the same using
directives to every file. C# 10 introduces global using directives, which allow you to define using
statements in one place and have them applied across the entire project. This reduces redundancy and makes code more maintainable.
How to Use Global Usings
To use global using directives, simply declare them in a .cs
file with the global
keyword:
global using System; global using System.Collections.Generic; global using System.Linq;
Once declared, these using
directives apply to all files within the project, so you no longer need to repeatedly include them in each file.
Example
Before C# 10, you might have had this at the top of every file:
using System; using System.Collections.Generic;
Now, you can define these globally in one place, and every file in the project will have access to them.
When to use global usings:
- Common namespaces like
System
,System.Linq
, andSystem.Collections.Generic
that are used across multiple files. - Project-specific namespaces that are needed everywhere, such as utilities or shared models.
3. File-Scoped Namespaces
In previous versions of C#, namespace declarations required curly braces, which added extra lines and indentation to your code. C# 10 introduces file-scoped namespaces, which remove the need for the curly braces and reduce indentation, making your files cleaner and more readable.
How to Use File-Scoped Namespaces
Instead of using this syntax:
namespace MyApp { class Program { static void Main(string[] args) { Console.WriteLine("Hello, World!"); } } }
You can now declare namespaces with a simpler syntax:
namespace MyApp; class Program { static void Main(string[] args) { Console.WriteLine("Hello, World!"); } }
With file-scoped namespaces, the namespace declaration applies to the entire file, and there’s no need for additional indentation.
When to use file-scoped namespaces:
- When you want cleaner code with less indentation, especially in large projects where deep nesting can be an issue.
- In projects where every file contains a single namespace, this simplifies the overall structure.
4. Improvements to Record Types
Records, introduced in C# 9, have become a favorite feature for developers due to their ability to easily create immutable data objects. C# 10 extends the functionality of records by allowing struct-based records and providing new ways to define and work with records.
Struct-Based Records
In C# 9, records were reference types (classes). Now in C# 10, you can create record structs, which are value types. This is useful when you need the performance benefits of value types combined with the immutability and concise syntax of records.
public readonly record struct Point(int X, int Y);
This creates a record struct with two properties, X
and Y
. Like classes, record structs support immutability and automatically generate equality comparisons, ToString()
, and Deconstruct()
methods.
Sealed ToString()
Method
In C# 10, the ToString()
method of a record is automatically marked as sealed
. This prevents unintended overrides of the ToString()
method when inheriting from a base record class.
5. Struct Improvements with with
Expression
The with
expression was previously only available for records, but in C# 10, it has been extended to support structs as well. This makes it easy to create a modified copy of a struct while keeping the original instance unchanged.
Example
public struct Point { public int X { get; init; } public int Y { get; init; } } var point1 = new Point { X = 10, Y = 20 }; var point2 = point1 with { X = 30 }; Console.WriteLine(point2); // Output: Point { X = 30, Y = 20 }
With the with
expression, you can copy and modify structs efficiently, while keeping the original instance immutable.
6. Improved Lambda Expressions
C# 10 introduces several enhancements to lambda expressions, making them more flexible and powerful. Here are the key improvements:
1. Natural Type for Lambdas
In previous versions of C#, you needed to explicitly declare the delegate type for lambdas, which could make the code verbose. In C# 10, lambda expressions can infer their type naturally, without needing an explicit delegate type declaration.
var add = (int x, int y) => x + y;
The add
variable is inferred as a Func<int, int, int>
based on the lambda signature.
2. Lambda Attributes
You can now apply attributes to lambda expressions, just like regular methods. This opens up new possibilities for integrating lambdas into frameworks and libraries that rely on attributes.
Func<int, int> square = [Obsolete] (int x) => x * x;
3. Lambda Return Type Inference
In C# 10, the return type of a lambda is inferred even when the body of the lambda is a block of statements.
Func<int, int> square = (int x) => { return x * x; };
Previously, such lambdas required explicit type declarations. Now, C# can infer the return type based on the block of statements.
7. Constant Interpolated Strings
In previous versions of C#, string interpolation couldn’t be used in constant expressions. C# 10 changes this by allowing constant interpolated strings, which means you can now use string interpolation in const
declarations.
Example
const string firstName = "John"; const string lastName = "Doe"; const string fullName = $"{firstName} {lastName}"; Console.WriteLine(fullName); // Output: John Doe
This is useful when you need to create constant values from other constants, making code cleaner and more expressive.
8. Nullable Reference Types Enhancements
Nullable reference types, introduced in C# 8, have made C# safer by helping developers avoid null reference exceptions. C# 10 enhances this feature with more granular annotations and improved support for nullable warnings in various scenarios.
Nullable Annotations in #nullable
Contexts
You can now enable or disable nullable reference types in specific sections of code using the #nullable
directive:
#nullable enable string? name = null; #nullable disable string nameWithoutWarning = null; // No warning because nullables are disabled
This allows developers to have more control over nullable contexts within their projects, reducing unnecessary warnings when working with legacy code or specific code sections.
9. Extended Property Patterns
Property patterns have been extended in C# 10 to allow nested property patterns directly within property pattern expressions. This simplifies complex pattern matching scenarios by allowing you to match nested properties inline.
Example
public class Address { public string City { get; set; } public string ZipCode { get; set; } } public class Person { public string Name { get; set; } public Address Address { get; set; } } var person = new Person { Name = "John", Address = new Address { City = "New York", ZipCode = "10001" } }; if (person is { Address: { City: "New York" } }) { Console.WriteLine("The person lives in New York"); }
With this enhancement, you can check deeply nested properties without having to write complex conditional logic, improving both readability and maintainability.
10. Performance and Miscellaneous Enhancements
In addition to these new language features, C# 10 also includes several performance improvements and smaller enhancements designed to optimize code execution and developer productivity.
Performance-Driven Changes
C# 10 and .NET 6 continue to optimize the performance of applications through improvements in memory management, JIT compilation, and garbage collection. These updates ensure that applications written in C# are more efficient and scalable, especially in high-performance environments like cloud-native and microservices architectures.
Other Enhancements
- Assignment and declaration in the same deconstruction statement: You can now declare variables while deconstructing them.
- Extension method support for
dynamic
: You can now use extension methods ondynamic
objects, improving flexibility when working withdynamic
types.
11. Conclusion
C# 10 brings a range of features that streamline development, enhance code readability, and improve performance. From global using directives and file-scoped namespaces to improvements in records, structs, and lambda expressions, C# 10 provides developers with the tools they need to write cleaner, more efficient code.
Whether you’re building enterprise-level applications, cloud services, or desktop software, mastering these new features will help you stay productive and maintainable as you develop in C#. With its continued evolution and focus on developer needs, C# remains one of the most powerful and versatile languages in 2024.