New Features In C# 12


A few C# 12 features have been made available as previews. The most recent versions of Visual Studio Preview and the.NET 8 Preview SDK preview allow you to test these capabilities.

  • Primary constructors
  • Using aliases for any type
  • Lambda expression parameters' default values

Primary constructors

Now, any class or struct can have primary constructors. The limitation on record types for primary constructors has been removed. The scope of the primary constructor parameters extends to the class as a whole. All expressly defined constructors must use this() syntax to invoke the primary constructor to guarantee that all primary constructor parameters are unquestionably assigned. A class's primary constructor prevents the compiler from declaring an implicit constructor without parameters. The implicit parameterless constructor sets the principal constructor parameters and all other fields to the 0-bit pattern in a struct.

Only in record types, either record class or record struct kinds, does the compiler produce public properties for primary constructor parameters. Non-record classes and structs may not always desire this behavior for primary constructor parameters.

Non-record classes and structs' primary constructors

Using aliases for any type Using default lambda expression parameters classes and structs, you can declare a primary constructor starting with C# 12. You put any parameters after the type name in brackets:

public class NamedItem(string name)
    public string Name => name;

The parameters of a primary constructor are in scope throughout the declaring type's entire body. They can set up properties or fields. They can be utilized in methods or local functions as variables. They are capable of being provided to a base constructor. The presence of a primary constructor indicates that these parameters are required for any instance of a type. To execute the primary constructor, any expressly specified constructor must utilize this(...) initializer syntax.

The implicit parameterless constructor is always emitted for any struct type, including record struct types, and always initializes all fields, including primary constructor parameters, to the 0-bit pattern. This ensures that all constructors have assigned the principal constructor parameters. When a primary constructor is present, the implicit parameterless constructor is not emitted for any class type, including record class types. The primary constructor must be invoked if you create an explicit parameterless constructor. In that situation, you can change the values of the primary constructor arguments. The following code demonstrates major examples.

If a primary constructor is read or written in the body of a non-record type, the compiler stores the parameter in a private field. The compiler generated a name for this private field that differs from the primary constructor argument. This ensures that the private field name does not clash with other variable names. No private field is captured if a primary constructor parameter is not used in the type's body. This prohibits allocating two copies of a primary constructor argument supplied to a base constructor by accident. It's also more difficult to locate when employing reflection.

// name isn't captured in Widget.
// width, height, and depth are captured as private fields
public class Widget(string name, int width, int height, int depth): NamedItem(name)
    public Widget() : this("N/A", 1,1,1) {} // unnamed unit cube

    public int WidthInCM => width;
    public int HeightInCM => height;
    public int DepthInCM => depth;

    public int Volume => width * height * depth;

The compiler generates public properties for each positional parameter in the primary constructor for record types (record class and record struct). If a primary constructor parameter for a record class type has the same name as a base primary constructor, that property is a public property of the base record class type. It does not appear in the derived record class type. Non-record types do not have these characteristics produced.

Lambda expression parameter's default values

C# 12 empowers lambda expressions by allowing you to set default values for parameters. The syntax is identical to that of other default parameters:

var addWithDefault = (int addTo = 2) => addTo + 1;
addWithDefault(); // 3
addWithDefault(5); // 6

Like all other default values, the default value is emitted as metadata and is accessible via reflection as the DefaultValue of the ParameterInfo of the lambda's Method property. As an example:

var addWithDefault = (int addTo = 2) => addTo + 1;
addWithDefault.Method.GetParameters()[0].DefaultValue; // 2

Before C# 12, you had to use a local function or the cumbersome System.DefaultParameterValue.Runtime. These ways continue to work, but they are more difficult to read and are inconsistent with the method's default settings. To give a default value for lambda expression parameters, use the InteropServices namespace. You'll have a consistent look for default parameter values on methods, constructors, and lambdas with the new default values on lambdas expression.

Using aliases for any type

With C# 12, any type can use directive support. Here are a few illustrations:

using Measurement = (string, int);
using PathOfPoints = int[];
using DatabaseInt = int?;

Nowadays, almost any type can be aliased. Nullable reference types cannot be aliased, but nullable value types can. Due to the possibility of including element names and types, tuples are particularly exciting:

using Measurement = (string Units, int Distance);

Anywhere a type would be used, aliases can be used instead. For instance:

public void F(Measurement x)
{ }

By aliasing types, you can abstract the actual types you're using and give confusing or lengthy generic and understandable names. Your code may be simpler to read as a result.

The What's New in C# 12 article has more information.

Allow the use of the alias directive to refer to any kind of Type.

using_alias_directive (13.5.2) to allow it to point to any type, not just named types. This would allow for types not currently permitted, such as tuple types, pointer types, array types, and so on. For example, the following would now be allowed:

using Point = (int x, int y);

Aliases for namespaces and named types (classes, delegated, interfaces, records, and structs) are possible in C#. This worked reasonably well because it allowed for the introduction of non-conflicting names in cases where a normal name pulled in from using_directives might be ambiguous, as well as the provision of a simpler name when dealing with complex generic types. However, adding more complex type symbols to the language has resulted in more use in places where aliases would be helpful but are currently not permitted. For example, tuples and function pointers frequently have lengthy and intricate regular textual forms that are difficult to read and painful to write out repeatedly. In these situations, aliases would be beneficial because they would provide a condensed name for the developer to use in place of the full structural forms.


C# 12 will include a slew of new developer capabilities, including records, enhanced pattern matching, async streams, top-level statements, and interpolated strings. These capabilities will make writing more efficient and expressive code easier, which will be especially valuable when designing real-time and data-driven apps. Whether you're a seasoned C# developer or just getting started, C# 12 is worth watching.


Q- What is  C# 12?

A- The future version of Microsoft's popular programming language, C#, is C# 12. It will include several new features designed to boost developer productivity and make building more efficient and expressive code easier.

Q- What are some of the new C# 12 features?

A- Records, enhanced pattern matching, async streams, top-level statements, and interpolated strings are among the new features in C# 12. These capabilities will assist developers in writing more efficient and expressive code, particularly in real-time and data-driven applications.

Q- When will C# 12 be available?

A- The release date for C# 12 has not yet been confirmed. However, it is scheduled to be included in the.NET 6 release in late 2021.

Q- What are the functions of records in C# 12?

A- Records are a new reference type in C# 12 intended to make writing immutable data structures easier. They are appropriate for usage in functional programming paradigms because they have value-based equality semantics.

Q- What are the advantages of async streams in C# 12?

A- In C# 12, async streams are a new form of asynchronous iterator that allows you to consume an asynchronous data stream. This functionality is very handy when dealing with real-time data sources such as sensors or IoT devices.

Similar Articles