The .NET Stacks #52: 🎂 Happy birthday to us

In this extended issue, we're recapping Build and walking through the release of .NET 6 Preview 4.

Dave Brock
Dave Brock

As if the completely ridiculous banner image didn't tip it off, it's true: today is the 1st birthday of The .NET Stacks! I'd like to thank our friend Isaac Levin—our first interview guest—for being such a good sport. If you haven't seen the wonderful "Application Development" keynote from Build, you should (and the picture will all make sense).

But most of all, I'd like to thank all of you for your support. Honestly, this little project started as a way to keep my mind busy during a pandemic lockdown and I really wasn't sure how things would go. (Looking back at the first issue ... that was very evident.) I'm thrilled it's been able to have the impact it has, and I'm grateful to all of you for that.

With all that out of the way, what are we talking about this week? In this extended issue, there's a lot here:  

  • Build 2021 recap
  • .NET 6 Preview 4 has arrived
  • Visual Studio updates
  • System.Console in .NET 7

Build 2021 recap

Last week, Microsoft rolled out Build 2021. You can check out the 330 sessions at the Build website, and there's a YouTube playlist at the Microsoft Developer YouTube channel. It's no secret that these days Build is heavy on promoting Azure services, but .NET got a lot of love last week, too.

Your mileage may vary, but my favorite sessions included the application development keynote, a .NET "Ask the Experts" session,  increasing productivity with Visual Studio, microservices with Dapr, modern app development with .NET, and a .NET 6 deep-dive session with Scott Hunter. (Hunter is Microsoft's CSO—the Chief Scott Officer. He also runs .NET.)

I want to call out a few interesting details from that session: updates on C# 10 and a new Blazor FluentUI component library that's taking shape. (There were other nice updates on .NET MAUI and Minimal APIs that we'll surely address in depth in later issues.)

C# 10 updates

In Scott Hunter's talk, Mads Torgersen and Dustin Campbell walked through some updates coming to C# 10. C# 10 looks to be focused on productivity and simplicity features. I want to show off record structs, required object initializers, auto-implemented property improvements, null parameter checking, global usings, and file-scoped namespaces.

Record structs

C# 9 brought us records, which gives you the ability to enforce immutability with the benefits of "value-like" behavior. While the C# 9 records are really just a class under the covers and accessed by reference, the "value-like" behaviors ensure that default equality checking works with your object's data (as opposed to reference equality). A good use case is with DTOs and other objects that benefit from immutability.

With all reference types, though, passing around a lot of records can create a lot of pressure on the garbage collector. If you couple that with using with expressions, copying and GC pressure can become an issue if you go crazy with records. Can we use structs with records? With C# 10, you can with the record struct syntax. It'll behave similarly, with the only key difference being that record structs aren't heap-allocated. This will also work with tuples, expressions, or any other struct types.

Let's look at some code, shall we? Let's say you have a Person record in C# 9:

record Person
   public string FirstName { get; init; }
   public string LastName { get; init; }

To use a record struct, change it to this:

record struct Person
   public string FirstName { get; init; }
   public string LastName { get; init; }

The default record declaration will still be a reference type. If you want to make that explicit, you can use the new class struct syntax. They are the same.

Required object initializers

I enjoy the flexibility of object initializers. You can use them to initialize objects however you want: you can initialize just a few properties, in whatever order you want, or none at all! Unfortunately, this flexibility can also bite you in the rear end if you aren't careful.

With C# 10, you can set fields to be required when performing object initialization, like this:

record struct Person
   public required string FirstName { get; init; }
   public required string LastName { get; init; }

Its early days on this feature, but it might also help to enforce whether types can be instantiated by positional syntax (constructors) or object initialization.

Auto-implemented property improvements

In the Build talk, Dustin and Mads talked about the cliff: let's say you want to change one little thing about an auto-implemented property. The next thing you know, you're creating a backing field, adding bodies for your getters and setters, and you're left wondering why it takes so much work to change one little thing.

With C# 10, you can refer to the auto-generated backing field without all that nonsense. You'll be able to work with a field keyword in your getters, setters, or even both.

record struct Person
   public string FirstName { get; init => field = value.Trim(); }
   public string LastName { get; init; }

This change provides better encapsulation and fewer lines of boilerplate code.

Null parameter checks

We've seen many nullability improvements over the last few C# releases, but null parameter checking can still be a manual chore—even with null reference types, you have to depend on the caller to do null checks.

With C# 10, this is taken care of with the !! keyword:

public void DoAThing(string text!!)

Don't worry, you aren't seeing double—this doesn't mean you're super excited about the text argument. Personally, I'm not a fan of the !!—at this rate, the C# team will need to start inventing new characters—but I am a fan of removing a bunch of this boilerplate nonsense.

Global usings and file-based namespaces

Lastly, the team introduced a few enhancements to help simplify your C# codebase.

With global usings, you can use the global using keywords to signify usings should be accessible throughout every .cs file in your project.

Here's a typical example of using statements you might want to use in your global using file:

global using System;
global using System.Collections.Generic;
global using System.Linq;
global using System.Threading.Tasks;
global using static System.Console;

I wonder if we could use Roslyn analyzers to shake out unused global usings for individual files. Anyway, I think this is a feature I will originally hate, then learn to love. It's nice to see what is being used, but after a while, it's a maintenance headache. This will be nice. (Not to mention ASP.NET Core developers are familiar with a similar approach with Razor files.) In any case, in many files, you might wind up with a global usings file, then individual usings for references that aren't scattered across your projects.

Lastly, the team introduced file-scoped namespaces. It allows you to go from this:

namespace MyNamespace
   record Person
      public string FirstName { get; init; }
      public string LastName { get; init; }

To this:

namespace MyNamespace;

record Person
   public string FirstName { get; init; }
   public string LastName { get; init; }

Of course, you could use top-level statements to remove namespaces completely—however, there are plenty of reasons why you don't want to abstract away your namespace declarations. In those cases, it's a nice, clean approach.

New Blazor component library

So here's something interesting that isn't getting a lot of attention: Microsoft is working on a component library for Blazor. Technically, these are wrappers around Microsoft's existing FluentUI Web Components and are built on FAST. You can fork the repository and browse to the examples app for a test page:

This is early, but I'd recommend taking a look—while it comes with the Blazor name, these are technically Razor components. This means you use them in other ASP.NET Core web apps, such as Razor Pages and MVC.

Microsoft customers have been asking for an in-house, free component library for a while—this will fill the need. While Microsoft will eventually introduce this as yet another tool at your disposal, they'll need to be careful here: the .NET community has a rich community of open-source and third-party component libraries (both free and paid), and they'll need to avoid the perception they're trying to replace these options. (To be abundantly clear, they definitely are not.)

.NET 6 Preview 4 has arrived

Just minutes into Build, Microsoft announced the official release of .NET 6 Preview 4. We've teased a few features in the last month or so, but it's nice to see the official news. Richard Lander wrote up the blog post. As a reminder, .NET 6 will be an LTS release.

.NET Hot Reload is a big .NET 6 feature (as is improving the developer inner loop in general). I've written about how you can use it by running dotnet watch with ASP.NET Core web apps (that is, Blazor, Razor Pages, and MVC). With Preview 4, you can also use it with other project types like WPF, Windows Forms, WinUI, console apps and "other frameworks that are running on top of the CoreCLR runtime." It's now integrated with the Visual Studio debugger as well—to do this, you need to download VS 2019 16.11 Preview 1.

In Lander's post, we also see much of what we've discussed previously: check out the official details on System.Text.Json improvements, LINQ enhancements, FileStream performance improvements on Windows, and new DateOnly and TimeOnly structs.

What about ASP.NET Core? ASP.NET Core is bringing it in this release—there's Minimal APIs, async streaming, HTTP logging middleware, improved SPA templates, Blazor error boundaries, and ... drum roll ... Blazor WebAssembly ahead-of-time (AOT) compilation! You can also start building .NET MAUI client-side apps with Blazor. Speaking of MAUI, there's a separate post outlining its Preview 4 updates. If you're using Entity Framework, make sure to check out that team's Preview 4 post to see all the wonderful perf improvements.

Preview 4 is a big one. Even with a little under six months to go, we'll only have a few previews to go until the focus turns to bug fixes. .NET 6 is coming along nicely.

Visual Studio updates

Last week, Microsoft also released Visual Studio 2019 v16.10 and v16.11 Preview 1.

With 16.10, we're seeing some more Git workflow improvements. The initial improvements to Git workflow in Visual Studio 2019 were a little rough, if we're being honest. It's nice to see the Visual Studio team listening to customer feedback and making it better. You can also now remove unused references—a long-adored ReSharper feature. In other news, there's improvements to Docker container tooling, IntelliSense completion improvements, Test Explorer improvements, and more. If F# is your jam, Phillip Carter announced some tooling updates for 16.10.

Also, if you're developing Azure Functions with the isolated worker in .NET 5, Azure Functions PM Anthony Chu has an update for you:

With 16.11 Preview 1, the big news is supporting hot reload in Visual Studio. We're also seeing .NET MAUI support.

On the topic of IDEs,  JetBrains released its roadmaps for ReSharper 2021.2 and Rider 2021.2.

Rethinking System.Console in .NET 7

With .NET 7—yes, .NET 7!—Microsoft is taking a look at redesigning System.Console.

As Adam Sitnik describes it, the initial design was driven by Windows OS capabilities and APIs. With .NET going cross-platform, it introduced a number of issues since there wasn't a good way to map Windows concepts to Unix. You're encouraged to follow the discussion and provide feedback.

We've seen a lot of community innovation in this space. For example, Patrik Svensson's Spectre.Console library shows us that the developer console experience can be productive and beautiful. This isn't lost and I'm interested to see how this work evolves.  

🌎 Last week in the .NET world

Welcome to Build week, where announcements are everywhere.

🔥 The Top 3

📢 Announcements

📅 Community and events

🌎 Web development

🥅 The .NET platform

⛅ The cloud

🔧 Tools

📱 Xamarin

🎤 Podcasts

🎥 Videos

.NET Stacks