C# 12 is the latest, just released, version of the popular programming language that runs on the .NET 8 platform. It introduces several new features that aim to improve the expressiveness, performance, and safety of the language. In this post, we will explore some of these features and see how they can benefit your code.
Those features might be used every day by developers adopting C# 12.
Primary constructors allow you to declare parameters for a class or struct in the same line as the type name. These parameters are in scope for the entire body of the type, and can be used to initialize fields, properties, or methods. For example:
class Student(string name, int score)
Primary constructors were previously available for record types, which are immutable types that support value equality and deconstruction. Record types can also use positional syntax to declare primary constructor parameters and public properties in one line. For example:
record Point(int X, int Y);
Public properties for primary constructor parameters are generated by the compiler only in record types, which can be either record class or record struct types.
Collection expressions are a new syntax to create collections of elements, such as arrays,
System.ReadOnlySpan<T>, or types supporting collection initializer like
List<T>. You can use the spread operator
.. to inline other collections into a collection expression. For example:
int someOtherNumbers = [4, 5, 6];
Collection expressions can be used anywhere you need a collection of elements, such as initializing a field, passing an argument, or returning a value.
You can now define default values for parameters in lambda expressions, just like you can for methods or local functions. This can make your code more concise and flexible when using delegates or functional programming patterns. For example:
var incrementBy = (int source, int increment = 1) => source + increment;
You can now use the
using directive to create aliases for any type, not just named types. This can be useful for creating semantic aliases for complex types, such as tuples, arrays, pointers, or other unsafe types. For example:
using Point = (int X, int Y);
Those features will be used mostly in advanced scenario of C# 12.
You can now use the
ref readonly modifier on parameters to indicate that they are passed by reference, but cannot be modified. This can improve the performance of your code by avoiding unnecessary copying, and also enforce the immutability of the arguments. For example:
var one = 1;
Inline arrays are a way to create an array of fixed size in a struct type, without using unsafe code or fixed size buffers. Inline arrays can improve the performance of your code by reducing allocations and copying. For example:
// Indicates that the instance's storage is sequentially replicated Length times.
Inline arrays are mainly used by the runtime team and other library authors to implement types such as
ReadOnlySpan<T>, which are used to represent slices of memory.
You can now mark types, methods, or assemblies with the
ExperimentalAttribute to indicate that they are experimental features that may change or be removed in the future. The compiler will issue a warning if you access a member that is marked with this attribute. For example:
Interceptors are an experimental feature that allow you to substitute a call to a method with a call to another method at compile time. This can be useful for modifying the semantics of existing code by adding new code to a compilation, for example in a source generator.
To use interceptors, you need to enable the
InterceptorsPreview feature flag in your project file.
Interceptors are still in preview mode and may be subject to breaking changes or removal in a future release.
These are the new features of C# 12 that you can start to use now. You can learn more about these features and other improvements in C# 12 by reading the official documentation or the feature specifications. Happy coding!