Concise code with C # 10

C # 10 was released at the same time as .NET 6, but can be used with restrictions in older .NET versions, even if not with official Microsoft support: “C # 10 is supported only on .NET 6 and newer versions.”

The current C # compiler version 4.0 is part of Visual Studio 2022 and the .NET 6 SDK. A separate download can be found in the NuGet package Microsoft.Net.Compilers. Package version 4.0.1 or higher is responsible for C # 10. The compiler can be used with Visual Studio for Mac 2022 as well as with a current version of Visual Studio Code and other OmniSharp-compatible editors.

Syntax innovations in C # 10 include shortened declarations and imports of namespaces, simplified lambda expressions and property patterns, changeable and unchangeable record structure types as an alternative to class-based records, constant interpolated strings as well as the mixture of assignments and mixed deconstruction .

All major innovations can be seen in Listing 1 for “Autor.cs” and Listing 2 for “Program.cs”, which are shown with line numbers for better assignment to the text. The program code is available for download as a Visual Studio project folder.

First of all, it should be mentioned that the author of this article introduces the new language features without evaluating them in detail. He deliberately stays out of the sometimes heated discussions in the developer community as to whether certain language features can be used.

Listing 1 shows in line 4 a namespace declaration at file level (file-scoped namespace). Instead of the previously mandatory declaration of a block in curly brackets

namespace Heise.Developer

{
 ...
}

A line with a semicolon at the end and without a block is sufficient. The namespace declaration at file level must appear before all type declarations. The simplification is based on the knowledge of the C # development team that only a small part of the publicly accessible C # files on GitHub use namespaces: “Measuring an even broader set of millions of C # files on GitHub shows literally 99.99% of files have just one namespace in them. “

From line 7, the type definition for the new type type follows in the declared namespace readonly record struct. Microsoft introduced Record Types in C # 9.0. Internally, these are classes that are on the heap, but have value semantics and create objects that cannot be changed in the standard. In C # 10 there is now also:

  • record class is synonymous with record without addition. As before, a class is created, i.e. a reference type on the heap. All properties created by the primary constructor have an init only setter. This means that the object is immutable unless the setter explicitly adds properties.
  • record struct stands for a structure – a type of value on the stack implicitly derived from System.ValueType inherits. Unlike one record class all properties created by the primary constructor have a normal setter. The object is therefore mutable.
  • readonly record struct also describes a structure that is derived from System.ValueType inherits. All properties created by the primary constructor have an init only setter: The object is immutable.

A primary constructor uses the notation in which the constructor parameters are listed directly after the class name:

public readonly record struct Autor(int ID, 
                    string Name, 
                    string Artikelstatus = "unbekannt") { }

It automatically creates public properties and assigns the transferred values.

The one used by the compiler for readonly record struct generated code (Listing 3) is similar to one record class. It offers properties with getters and init only setters included Equals()-Implementation, operator overload for == and !=, Output of all data members at ToString() as well as a Deconstruct()-Implementation.

It should be noted that with a record struct the fields and properties that are not part of the primary constructor must be explicitly initialized. At a readonly record struct all additional data members must have the addition readonly exhibit. A value initialization in the declaration is, as with all members, with the addition readonly permitted. In contrast to a record class, a record structure can neither be a parent nor a child type.

The additional property CheckObjektErzeugungsZeitpunkt has the data type DateOnlywho as well as TimeOnly is new in .NET 6 and the previous data type DateTime extended by only saving the date and time. Even if both types are not strictly speaking a language feature of C # 10, they fit into the context of the example.

In addition to the caller info annotations introduced in C # 5.0 [CallerFilePath], [CallerLineNumber] and [CallerMemberName] C # 10 now knows Caller Argument Expressions, with which a method receives the information which expressions (variable names or formulas) are behind the values ​​passed by the caller. The annotation in the parameter list is used for this System.Runtime.CompilerServices.CallerArgumentExpressionAttributewhich has existed since .NET Core 3.0.

The method in lines 15 to 30 in Listing 1 has four parameters: two “real” parameters and two caller argument expressions for the first two parameters. The Caller Argument Expressions refer to the name of the parameter. Unfortunately you have to enter the parameters as a string, and the operator nameof() it does not work.

Calling the method only expects the first two parameters:

  obj.CheckObjektErzeugungsZeitpunkt(new DateOnly(2010,1,1), 
                         DateOnly.FromDateTime(DateTime.Now));

The compiler fills the rest automatically. The program acknowledged the above call on November 15 with the following runtime error message:

01.06.2006 muss zwischen 01.01.2010 (new DateOnly(2010,1,1))
und 15.11.2021 (DateOnly.FromDateTime(DateTime.Now)) liegen!
(Parameter 'ObjektErzeugungsZeitpunkt')

The method learns that the call specified a static value (01/01/2010) for the first parameter and always returns the current date for the second parameter.

To home page