We are pleased to Release.NET 6 RC(Release Candidate) 2. It is the second of two “Go Live” release candidates supported in production environments. Over the past few months, the team has been focusing on quality improvements. There are a lot of new features in this release, but we’re not fully integrating them until near the end. The team is currently validating end-to-end workflows to find areas where design intent and technical reality do not yet match up perfectly. This results in the maintenance staff on the team having to fix bugs to do this.

We’ve heard from our users about upgrading the production site to. NET 6 is both “boring “(non-event) and” exciting “(significant performance improvements). Unsurprisingly, we believe RC2 will continue this trend.

You can download.NET 6 Release Candidate2 for Linux, macOS, and Windows.

  • Setup program and binaries
  • Container mirror
  • Linux package
  • The release notes
  • API diff
  • Known problems
  • GitHub Problem tracker

See the.net MAUI and ASP.NET Core posts for more details on the new features in client and Web application scenarios.

The.NET Conference is a free, three-day event for virtual developers to celebrate. A major version of.net. This year,.NET 6 will be released at the.NET Conference November 9-11. We expect you to note the date and tune in on time. Visual Studio 2022 Preview 5 was also released today, and they also announced a release event on November 8th. You can read all about it on the Visual Studio blog.

We are in this interesting part of the cycle to support the production of new releases. We sincerely encourage it. If you need guidance on how to handle this, you can contact us at [email protected]. Many companies have already started contact, and some have already started production. We also helped the Microsoft team run on the RC release. Several Microsoft teams are already working on RC1, and we look forward to more help with RC2. This includes.net sites.

.NET 6 RC2 has been tested and is supported by Visual Studio 2022 Preview 5, also released today. Visual Studio 2022 will support. NET 6, which Visual Studio 2019 does not support. Again, MSBuild 17.x will support it, while 16.x will not. If you want to use.NET 6, you will need to upgrade to Visual Studio 2022.

Support for Mac versions of Visual Studio 2022 is currently available with. NET 6 RC2 incompatible. We’re working on it.

Check out the new conversation posts for an in-depth engineer-to-engineer discussion of the latest.NET features.

.NET 6 RC1 posts focus on basic features, many of which won’t be fully implemented until.NET 7. This article focuses on C# 10 and related improvements to templates. It also includes updates (including major changes) to Arm64 for macOS and Windows. Let’s take a look.

C#10

C# 10 is an important part of the.net 6 release. For the most part, C# 10 is a further evolution of existing concepts and features such as records and patterns. It also includes features — global usage and file-scoped namespaces — that can help you simplify your code and write fewer boilerplate files. These specific improvements are the basis for the template changes discussed later in this article. You can see the examples used in this section in.net 6 examples. I will add more examples in my final.net 6 post.

Record structs C# 10 adds support for Record structs. This new feature is similar to C# 9(class-based) recording, but with some major differences. In most cases, Record structs are added for completeness, so that structures can enjoy the same recording advantages as classes. However, rather than simply structuring the records, the team decided to align the structural records with ValueTuple, just as the class records match. Because of this design approach, Record structs properties are mutable by default, whereas Record class properties are immutable. However, you can declare a read-only Record structs that is immutable and matches Record class semantics.

At a high level, Record Structs do not replace the Record class, nor do we encourage migrating the Record class to Record Structs. The instructions for using the class VS structure also apply to the Record class and Record Structs. In other words, you should choose between a class and a structure before choosing to use Record.

Let’s look at how Record Structs differs from the Record class

Battery battery = new("CR2032".0.235.100);
WriteLine(battery);
while (battery.RemainingCapacityPercentage > 0)
{
    battery.RemainingCapacityPercentage--;
}
WriteLine(battery);
public record struct Battery(string Model, double TotalCapacityAmpHours, int RemainingCapacityPercentage);
Copy the code

This example of a Record structs produces the following result

Battery { Model = CR2032, TotalCapacityAmpHours = 0.235, RemainingCapacityPercentage = 100 }
Battery { Model = CR2032, TotalCapacityAmpHours = 0.235, RemainingCapacityPercentage = 0 }
Copy the code

As mentioned earlier, the most obvious difference is that record structure attributes are mutable by default. This is the main difference apart from struct and record struct syntax. I also rewrote the example with a read-only record structure, as shown below.

Battery battery = new("CR2032".0.235.100);
WriteLine(battery);
while (battery.RemainingCapacityPercentage > 0)
{
    Battery updatedBattery = battery with {RemainingCapacityPercentage = battery.RemainingCapacityPercentage - 1};
    battery = updatedBattery;
}
WriteLine(battery);
public readonly record struct Battery(string Model, double TotalCapacityAmpHours, int RemainingCapacityPercentage);
Copy the code

You’ll see that it’s almost identical to the (class)Record example I published for C# 9.

Let’s review the record for C# 9. They provide a concise syntax for defining data-oriented classes with similar structures. They favor immutability while providing concise syntax – with expressions – for immutable and friendly replication. People might be surprised that we’re starting to use classes to implement structure-like functionality. Most of the time, developers use classes instead of structures because of reference-passing rather than value semantics. In most cases, classes are the best choice and easy to use.

Structural records are very similar to class records:

  • They use the same syntax (except for struct or class in the definition).
  • They allow custom member definitions (new in C#10) to use fields on attribute members (by default).
  • They allow you to customize member behavior using init or mutable properties.
  • They support expressions. In fact, all structural types since C# 10 support expressions.

Structural records differ from class records:

  • Record structs can be defined as Record structs or read-only Record structs.
  • A record class is defined with a record or record class.
  • By default, the Record structs property is mutable (get/set).
  • By default, record class attributes are immutable (get/init).

The asymmetric (non-) variability behavior between Record structs and the Record class may surprise, or even disgust, some readers. I’ll try to explain the thinking behind the design. Because semantics are passed through values, structures hardly benefit as much from immutability as classes. For example, if a structure is a key in a dictionary, the key (a copy of the structure) is immutable and can only be looked up by equality. At the same time, the design team thought that ValueTuple could be viewed as an anonymous Record structure, and Record structs could be viewed as a derivative of ValueTuple. This only works if the record structure contains variability and support fields, which is what the team decided to do. In addition, inclusion variability reflects structural and class differences.

If you prefer the immutable behavior of a record structure, you can get it by adding the readonly keyword.

Looking at structs generally, key functionality is common:

  • The equivalence checks provided by the runtime are the same for all structures.
  • All constructs can be used with expressions to create non-mutated copies, a new feature in C# 10.

Global usings

Global using lets you specify a namespace that you want available in the source file as if it were declared in each source file. It can also be used with statics and aliases. This feature allows a common set of using declarations to be extended to more using lines that are no longer needed. This is most relevant to namespaces, but can be used for any namespace.

The following syntax can be used in various forms of use:

  • global using System;
  • global using static System.Console;
  • global using E = System.Environment;

These declarations only need to be declared once in the compile. There are four patterns that declare global using.

  • In program.cs, root using statements are made global to the entire Program by upgrading them to global using.
  • Centrally manage all global Using statements in the globaluses.cs file (or similar names).
  • In the project file, use the following syntax.
  • In your project file, use the following syntax to enable the default platform (for the MSBuild SDK on which your application depends).

The following MSBuild syntax can be used in place of the.cs file (using a similar approach to the previous example).

  • < Using Include=”System”/ >
  • < Using Include=”System.Console” Static=”True”/ >
  • < Using Include=”System.Environment” Alias=”E”/ >

You can enable the platform defined implicit using statement in < PropertyGroup >.

  • < ImplicitUsings > enable < /ImplicitUsings >

Implicitly use different MSBuild SDKS. Microsoft.NET.Sdk defines a base set, and other SDKS define add-on sets.

If you use the MSBuild feature, you can see valid results in the files generated in the obj directory, as shown below.

PS C:\Users\rich\app> type .\app.csproj | findstr Using
    <ImplicitUsings>enable</ImplicitUsings>
    <Using Include="System.Console" Static="True"/>
    <Using Include="System.Environment" Alias="E"/>
PS C:\Users\rich\app> type .\obj\Debug\net6. 0\app.GlobalUsings.g.cs
// <auto-generated/>
global using global::System;
global using global::System.Collections.Generic;
global using global::System.IO;
global using global::System.Linq;
global using global::System.NET.Http;
global using global::System.Threading;
global using global::System.Threading.Tasks;
global using E = global::System.Environment;
global using static global::System.Console;
Copy the code

As you can see, Microsoft t.net.sdk has added several globalusing statements to the.globalusings.g.s file. Note that the global :: operator is not required and can be safely ignored for the sake of understanding the generated syntax.

File and namespace declarations

File-scoped namespace declarations are another C# 10 feature designed to reduce indentation and line count.

The syntax for this function is: namespaceFoo;

It is an alternative to the traditional three-line syntax: namespaceFoo {}

Three lines of syntax can be nested. Single-line syntax does not support nesting. Each file can have only one declaration of a file scope. It must precede all types defined in the file, much like the three-line syntax.

Namespaces are incompatible with top-level statements. Top-level statements exist in top-level namespaces. This also means that if you add additional methods to the Program class using partial class syntax, the Partial Program class also needs to be in the top-level namespace.

This feature is very similar to the single-line using declaration added to C# 8.

Const and interpolated strings

Now you can assign an interpolated string to a const variable. Interpolation strings are intuitive to use and read, and can be used anywhere. They can now be used with const, provided that the placeholder value is also constant.

The following example demonstrates a possible example:

const string Bar = "Bar";
const string DoubleBar = $"{Bar}_{Bar}";
WriteLine(DoubleBar);
Copy the code

String interpolation has many other improvements, described in string interpolation in C# 10 and.net 6.

Extended attribute pattern

You can now reference nested properties or fields in properties mode. For example, the following schema is now legal:

{Prop1.Prop2: pattern }

Previously, you needed to use a more detailed form:

{Prop1:{Prop2: pattern }}

You can see the use of this more compact form in the example below, for example using Reading.pm25.

List<Status> statuses = new()
{
    new(Category.Normal, new(20.false.20)),
    new(Category.Warning, new(20.false.60)),
    new(Category.Danger, new(20.true.60)),
    new(Category.Danger, new(100.false.20))};foreach (Status status in statuses)
{
    string message = status switch
    {
        {Category: Category.Normal} => "Let the good times roll",
        {Category: Category.Warning, Reading.PM25: >50 and <100} = >"Check the air filters",
        {Reading.PM25: >200} = >"There must be a fire somewhere. Don't go outside.",
        {Reading.SmokeDetected: true} = >"We have a fire!",
        {Category: Category.Danger} => "Something is badly wrong", _ = >"Unknown status"
    };
    Console.WriteLine(message);
}
record struct Reading(int Temperature.bool SmokeDetected.int PM25);
record struct Status(Category Category.Reading Reading);
enum Category
{
    Normal,
    Warning,
    Danger
}
Copy the code

.net SDK: a modern C# project template

We updated it in Preview 7. NET SDK template, using the latest C# features and patterns. There was significant feedback on these changes, in part because the build started to fail. As part of the initial template update, we default to. NET6(NET 6.0) projects (including from NET 5 updated to. NET 6 applications) enable implicit use (aka opt-out). That has changed. We’ve updated the SDK, so all the new features are optional. The response to this change (in RC1) has been positive.

There was also feedback that some people didn’t like the new simplified program.cs file, which had top-level statements. We improved the top-level statement and continued to use it for templates. We hope that most developers who prefer traditional methods will simply add additional methods themselves.

The following language features are used in the new template:

  • Asynchronous Main
  • Top statements
  • Target-typed new expression
  • Global using instruction
  • File scoped namespace
  • Nullable reference type

We built all of these features because we thought they were better than the previous alternatives. Templates are the simplest and best way to guide new developers and new applications to use the best patterns. The C# design team firmly believes in using fewer lines, fewer characters to specify a given concept or operation, and reducing unnecessary repetition. That’s what most of these new features support. Nullable is different in that it produces more reliable code. Every application or library that uses Nullable is less likely to crash in production. Software is too complex for humans to see errors the way a compiler does.

A common theme of these features is that they reduce distractions and add hints when you view code in the code editor. Now, more important aspects will pop up. For example, if you see a USING statement in a given file, it is a special statement that is required outside of the implicit set. You should be able to copy/paste code from one file to another without CTRL-. Type to add the required namespace (at least not that much). If you see nullable warnings or errors, you know that your code may be incorrect in some way. There are benefits to getting rid of indentation. More code will be visible in the first dozen columns of the editor, rather than just filling those columns with space characters. One can think of these features as conveying a higher density of specificity and meaning to the eye in the process of conveying information to the brain.

Console template

Let’s start with the console template. This is very small.

Program.cs:

// See https://aka.ms/new-console-template for more informationConsole.WriteLine(&quot; Hello, World! &quot;) ;Copy the code

Project files:

<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net6.0</TargetFramework>  <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> </Project>Copy the code

Although the console template is better than its. The corresponding template for NET 5 is much smaller, but no less functional. You can also see that ImplicitUsings is now an optional feature and is enabled in the template.

Web template

Web templates are also small.

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () = >"Hello World!");
app.Run();
Copy the code

Webapi templates are closer to a typical ASP.NET Core application. You will soon notice that the separation between program.cs and startup. cs has been removed. Startup.cs is no longer required.

Program.cs:

var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
/ / learn more about you Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
Copy the code

The project file for the WebAPI template is similar to the console.

<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <TargetFramework>net6. 0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Swashbuckle.AspNetCore" Version=6.1.5 "" />
  </ItemGroup>
</Project>
Copy the code

You can see file scope namespaces used in this example, including in weatherforecast.cs:

namespace webapi; public class WeatherForecast { public DateTime Date { get; set; } public int TemperatureC { get; set; Public int TemperatureF => 32 + (int TemperatureC / 0.5556); public string? Summary { get; set; }}Copy the code

New target type and WeatherForecastController. Cs used together:

private static readonly string[] Summaries = new[]
{
    "Freezing"."Bracing"."Chilly"."Cool"."Mild"."Warm"."Balmy"."Hot"."Sweltering"."Scorching"
};
Copy the code

In MVC templates, you can see the use of nullable annotations and expression body methods.

namespace webmvc.Models;
public class ErrorViewModel
{
    public string? RequestId { get; set; }

    public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
}
Copy the code

Windows Forms template

The Windows Forms template has also been updated. It includes most of the other improvements covered by other templates, although it is not a top-level statement.

namespace winforms;
static class Program
{
    /// <summary>
    ///  The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main()
    {
        ApplicationConfiguration.Initialize();
        Application.Run(newForm1()); }}Copy the code

You can see that single-line namespace statements are used, and because implicit use is enabled, there are no platform-level USING statements.

The WPF template has not been updated as part of the release.

The implicit use

Now I’ll show you these features in action. Let’s start with implicit usage. When enabled, each Sdk adds its own set of implicit USING statements.

As mentioned earlier, Microsoft.NET.Sdk adds several global using statements.

If you disable this feature, you will see that the application no longer compiles because the System namespace is no longer declared (in this case).

PS C:Usersrichapp> type .app.csproj | findstr Implicit
    <ImplicitUsings>disable</ImplicitUsings>
PS C:Usersrichapp> dotnet build
Microsoft (RBuild Engine version 17.0.0-Preview -21501-01+ bbCCe1dFFfor .NET
Copyright (C) Microsoft Corporation. All rights reserved.
  Determining projects to restore...
  All projects are up-to-date for restore.
  You are using a preview version of .NET. See: https://aka.ms/dotnet-core-preview
C:UsersrichappProgram.cs(2.1): error CS0103: The name 'Console' does not exist in the current context [C:Usersrichappapp.csproj]
Build FAILED.
Copy the code

Another option is to use the Global Using feature, which allows you to use only the namespaces you want, as shown in the following example.

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6. 0</TargetFramework>
    <ImplicitUsings>disable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>
  <ItemGroup>
    <Using Include="System" />
  </ItemGroup>
</Project>
Copy the code

Now, build the code.

PS C:Usersrichapp> type .app.csproj | findstr Using <ImplicitUsings>disable</ImplicitUsings> <Using Include="System" /> PS C:Usersrichapp> dotnet build Microsoft (R) Build Engine version 17.0.0-preview-21501-01+ bbcce1dFF for.net Copyright (C) Microsoft Corporation. All rights reserved. Determining projects to restore... All projects are up-to-date for restore. You are using a preview version of .NET. See: https://aka.ms/dotnet-core-preview - > C: app UsersrichappbinDebugnet6.0 app. DLL Build succeeded. 0 Warning (s) 0 Error (s) The Time Elapsed 00:00:00. 80Copy the code

It’s not recommended as a universal model, but it’s a good choice for people who want maximum control. In most cases, we expect developers to rely on the implicit use provided by the SDK and take advantage of the explicit global using of namespaces from their own code or from the commonly used NuGet packages.

Nullable sex

I have updated program.cs to demonstrate nullable reference types. The application calls the List< T > method, which returns a T? , in this case, is a nullable string (string?)

List<string> greetings = new()
{
    "Nice day, eh?"
};
string? hello = greetings.Find(x => x.EndsWith("!"));
string greeting = hello ?? "Hello world!";
Console.WriteLine($"There are {greeting.Length} characters in "{greeting}"");
Copy the code

The line that defines the hello variable if it’s not defined as var, string? Or List< T >.Find returns null and will not compile. If I hadn’t enabled the Nullable feature, I would have missed this problem, which would have caused my code to crash with a NullReferenceException. That’s not good. I use?? On the next line. And null merge operators. In most cases, these two lines of code are combined into one line, as shown below. Considering apis that return nullable reference types, I split them up (in this artificial example) so that you can see the string I use?

string greeting = greetings.Find(x =\&gt; x.EndsWith(&quot; ! &quot;) )?? &quot; Hello world! &quot;;

In this case, I combined everything into one line. I can now declare variables as strings because NULL has adapted?? The string that follows. The string? In this case, only the compiler can see it.

string[] args

Now, I’ll quickly address the questions most people have about top-level statements, including args parameters. The args parameter is still available in the top-level statement. It’s just not obvious. Here is another program that demonstrates how to use it.

string greeting = args.Length > 0 ? string.Join("", args) : "Hello World!";
WriteLine($"There are {greeting.Length} characters in "{greeting}" in this {nameof(Program)}.");
Copy the code

It produces the following results.

PS C:\Users\rich\app> dotnet run
There are 12 characters in "Hello World!" in this Program.
PS C:\Users\rich\app> dotnet run Nice day, eh?
There are 13 characters in "Nice day, eh?" in this Program.
Copy the code

The application also demonstrates that the Program type is still defined. The Program. The Main is not.

I removed Console from console. WriteLine in program.cs by adding a global static using to my project file, as shown below.

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6. 0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>
  <ItemGroup>
    <Using Include="System.Console" Static="True"/>
  </ItemGroup>
</Project>
Copy the code

Define general methods

We’ve heard some feedback from users that real methods are better than local functions, especially for the Program class. You can use any model with top-level statements.

I left the Program unchanged, but switched all functionality to static methods in the Program class, defined in part classes.

Program.cs.
WriteLine(GetTheGreeting(args));
Program.Foo.cs.
public partial class Program
{
    public static string GetTheGreeting(string[] args)
    {
        string greeting = args.Length > 0 ? string.Join("", args) : "Hello World!";
        return $"There are {greeting.Length} characters in "{greeting}" in this {nameof(Program)}."; }}Copy the code

It produces the same results as the previous example. The file name of program.foo. cs is arbitrary. In fact, the same is true of program.cs. You can name the documents whatever you like. The compiler will find them.

As mentioned earlier, when using top-level statements, the Program type must be in the top-level namespace.

MacOS and Windows Arm64 update

In addition to good news, we also have some groundbreaking changes to share. The project to support macOS and Windows ARM64 is almost complete. We’ve been working on this for over a year now with the help of the macOS and Windows teams. Initially, we thought the project was just for Arm64 support on macOS, and Rosetta 2 would cover X64. It’s easy. It’s like ISAs, right? Not exactly. As we delve deeper, we find that x64 + Arm64 coexistence is a much larger task, focusing on the CLI and the installer, including some areas where they need to agree. With RC2, we are delivering a build of.NET to provide the best experience we can imagine.

I’ll give a quick summary of our processes for macOS and Windows Arm64 Machines:

  • .NET 6 RC2 implements Arm64 + X64 coexistence by installing the Arm64 and X64 versions in different locations. So far, Arm64 and X64 build each other, which led to General Sadness.
  • You need to uninstall all.NET versions and start from scratch (on macOS and Windows Arm64 machines) to adopt.NET 6 RC2+. This includes Arm64 and X64,.net 6 and prior to.NET 6.
  • Versions prior to.NET 6 are not ready for installation.
  • The CLI allows you to use the Arm64 SDK for Arm64 and X64 development (assuming you have the required Arm64 and X64 runtimes installed). The reverse is also true.
  • We want people to just use the Arm64 SDK because it will have a better experience (native architecture performance; There is only one SDK to maintain). We will continue to improve the product so that this model is an easy choice for most developers.
  • For the SDK, we only support it on Arm64. NET + 6. Early SDK builds will be blocked on Arm64.
  • For the runtime, we will support all supported versions, Arm64 and X64.
  • .NET 6 RC2 provides a lot of Arm64(including X64 emulation). NET 6 Final experience.
  • We want to update.net Core 3.1 and. NET 5 runtime with. NET 6 RTM remains consistent (both in terms of time and technology). That’s still to be determined.
  • RC2’s night build is currently broken, so you’ll need to wait a few more weeks until we actually release RC2 to try it all out.
  • Windows Arm64. The NET 5 SDK will follow. NET 6 RTM and early withdrawal from support.

We are also considering some radical changes to dotnet Test to unify our vision for architectural goals:

  • [Breaking change] For dotnet test, switch-a to alias — arch instead of — test-adapter-path
  • [Breaking change] For dotnet test, switch-r to alias — Runtime instead of — results-dir

A large part of the project is enabling the X64 runtime through the Arm64 SDK. You can see this in the figure below.

For more details, check out dotnet/ SDK #21686.

conclusion

C# 10 builds on similar features of C# 9 and offers significant improvements in simplicity and expressiveness. In the past, one could reasonably scoff at C# because so much etiquette and knowledge of object-oriented concepts was required to write just one line of code. Those days are over, and the template reflects that. Part of the motivation for these changes is to make C# more attractive to new programmers and schools. These simplified changes fundamentally change what you need to start learning and becoming proficient with C#. In its new default form, it can be directly compared to other languages that similarly start with a single file source file.

We look forward to seeing other projects simplify their new user experiences with top-level statements, global usage, logging, and many other modern features. There are many opportunities to create an easier learning journey for open source projects and commercial products.

We encourage you to use.NET 6 RC2. We believe you will find it a reliable release. And the global.NET Conf date will be November 9th for.net 6 release.

Thank you for being a.NET developer.