Today, we are shipping .NET 5.0 Release Candidate 1 (RC1). It is a near-final release of .NET 5.0, and the first of two RCs before the official release in November. RC1 is a “go live” release; you are supported using it in production. At this point, we’re looking for reports of any remaining critical bugs that should be fixed before the final release. We need your feedback to get .NET 5.0 across the finish line.
We also released RC1 versions of ASP.NET Core and EF Core today.
You can download .NET 5.0, for Windows, macOS, and Linux:
- Installers and binaries
- Container images
- Snap installer
- Release notes
- Known issues
- GitHub issue tracker
You need the latest preview version of Visual Studio (including Visual Studio for Mac) to use .NET 5.0.
.NET 5.0 includes many improvements, notably single file applications, smaller container images, more capable JsonSerializer APIs, a complete set of nullable reference type annotations, new target framework names, and support for Windows ARM64. Performance has been greatly improved, in the NET libraries, in the GC, and the JIT. ARM64 was a key focus for performance investment, resulting in much better throughput and smaller binaries. .NET 5.0 includes new language versions, C# 9 and F# 5.0.
We recently published a few deep-dive posts about new capabilities in 5.0 that you may want to check out:
- F# 5 update for August
- ARM64 Performance in .NET 5
- Improvements in native code interop in .NET 5.0
- Introducing the Half type!
- App Trimming in .NET 5
- Customizing Trimming in .NET 5
- Automatically find latent bugs in your code with .NET 5
Just like I did for .NET 5.0 Preview 8 I’ve chosen a selection of features to look at in more depth and to give you a sense of how you’ll use them in real-world usage. This post is dedicated to records in C# 9 and System.Text.Json.JsonSerializer
. They are separate features, but also a nice pairing, particularly if you spend a lot of time crafting POCO types for deserialized JSON objects.
C# 9 — Records
Records are perhaps the most important new feature in C# 9. They offer a broad feature set (for a language type kind), some of which requires RC1 or later (like record.ToString()
).
The easiest way to think of records is as immutable classes. Feature-wise, they are closest to tuples. One can think of them as custom tuples with properties and immutability. There are likely many cases where tuples are used today that would be better served by records.
If you are using C#, you will get the best experience if you are using named types (as opposed to a feature like tuples). Static typing is the primary design point of the language. Records make it easier to use small types, and take advantage of type safety throughout your app.
Records are immutable data types
Records enable you to create immutable data types. This is great for defining types that store small amounts of data.
The following is an example of a record. It stores user information from a login screen.
public record LoginResource(string Username, string Password, bool RememberMe);
It is semantically similar (almost identical) to the following class. I’ll cover the differences shortly.
public class LoginResource
{
public LoginResource(string username, string password, bool rememberMe)
{
Username = username;
Password = password;
RememberMe = rememberMe;
}
public string Username { get; init; }
public string Password { get; init; }
public bool RememberMe { get; init; }
}
init
is a new keyword that is an alternative to set
. set
allows you to assign to a property at any time. init
allows you to assign to a property only during object construction. It’s the building block that records rely on for immutability. Any type can use init
. It isn’t specific to records, as you can see in the previous class definition.
private set
might seem similar to init
; private set
prevents other code (outside the type) from mutating data. init
will generate compiler errors when a type mutates a property accidentally (after construction). private set
isn’t intended to model immutable data, so doesn’t generate any compiler errors or warnings when the type mutates a property value after construction.
Records are specialized classes
As I just covered, the record and the class variants of LoginResource
are almost identical. The class definition is a semantically identical subset of the record. The record provides more, specialized, behavior.
Just so we’re on the same page, the following comparison is between a record
, and a class
that uses init
instead of set
for properties, as demonstrated earlier.
What’s the same?
- Construction
- Immutability
- Copy semantics (records are classes under the hood)
What’s different?
- Record equality is based on content. Class equality based on object identity.
- Records provide a
GetHashCode()
implementation that is based on record content. - Records provide an
IEquatable<T>
implementation. It uses the uniqueGetHashCode()
behavior as the mechanism to provide the content-based equality semantic for records. - Record ToString() is overridden to print record content.
The differences between a record and a class (using init
) can be seen in the disassembly for LoginResource as a record and LoginResource as a class.
I’ll show you some code that demonstrates these differences.
Note: You will notice that the LoginResource
types end in Record
and Class
. That pattern is not the indication of a new naming pattern. They are only named that way so that there can be a record and class variant of the same type in the sample. Please don’t name your types that way.
This code produces the following output.
rich@thundera records % dotnet run
Test record equality -- lrr1 == lrr2 : True
Test class equality -- lrc1 == lrc2 : False
Print lrr1 hash code -- lrr1.GetHashCode(): -542976961
Print lrr2 hash code -- lrr2.GetHashCode(): -542976961
Print lrc1 hash code -- lrc1.GetHashCode(): 54267293
Print lrc2 hash code -- lrc2.GetHashCode(): 18643596
LoginResourceRecord implements IEquatable<T>: True
LoginResourceClass implements IEquatable<T>: False
Print LoginResourceRecord.ToString -- lrr1.ToString(): LoginResourceRecord { Username = Lion-O, Password = jaga, RememberMe = True }
Print LoginResourceClass.ToString -- lrc1.ToString(): LoginResourceClass
Record syntax
There are multiple patterns for declaring records that cater to different use cases. After playing with each one, you start to get a feel for the benefits of each pattern. You’ll also see that they are not distinct syntax but a continuum of options.
The first pattern is the simplest one — a one liner — but offers the least flexibility. It’s good for records with a small number of required properties.
Here is the LoginResource record, shown earlier, as an example of this pattern. That’s it. That one line is the entire definition.
public record LoginResource(string Username, string Password, bool RememberMe);
Construction follows the requirements of a constructor with parameters (including the allowance for optional parameters).
var login = new LoginResource("Lion-O", "jaga", true);
You can also use target typing if you prefer.
LoginResource login = new("Lion-O", "jaga", true);
The next syntax makes all the properties optional. There is an implicit parameterless constructor provided for the record.
public record LoginResource
{
public string Username {get; init;}
public string Password {get; init;}
public bool RememberMe {get; init;}
}
Construction uses object initializers and could look like the following:
LoginResource login = new()
{
Username = "Lion-O",
TemperatureC = "jaga"
};
Maybe you want to make those two properties required, with the other one optional. This last pattern would look like the following.
public record LoginResource(string Username, string Password)
{
public bool RememberMe {get; init;}
}
Construction could look like the following, with RememberMe
unspecified.
LoginResource login = new("Lion-O", "jaga");
And with RememberMe
specified.
LoginResource login = new("Lion-O", "jaga")
{
RememberMe = true
};
I want to make sure that you don’t think that records are exclusively for immutable data. You can opt into exposing mutable properties, as you can see in the following example that reports information about batteries. Model
and TotalCapacityAmpHours
properties are immutable and RemainingCapacityPercentange
is mutable.
It produces the following output.
Produces the following output:
rich@thundera recordmutable % dotnet run
Battery { Model = CR2032, TotalCapacityAmpHours = 0.235, RemainingCapacityPercentage = 100 }
Battery { Model = CR2032, TotalCapacityAmpHours = 0.235, RemainingCapacityPercentage = 0 }
Non-destructive record mutation
Immutability provides significant benefits, but you will quickly find a case where you need to mutate a record. How can you do that without giving up on immutability? The with
expression satisfies this need. It enables creating a new record in terms of an existing record of the same type. You can specify the new values that you want to be different, and all other properties are copied from the existing record.
Let’s transform the username to lower-case. That’s how usernames are stored in our pretend user database. However, the original username casing is required for diagnostic purposes. It could look like the following, assuming the code from the previous example:
LoginResource login = new("Lion-O", "jaga", true);
LoginResource loginLowercased = login with {Username = login.Username.ToLowerInvariant()};
The login
record hasn’t been changed. In fact, that’s impossible. The transformation has only affected loginLowercased
. Other than the lowercase transformation to loginLowercased
, it’s identical to login
.
We can check that with
has done what we expect using the built-in ToString()
override.
Console.WriteLine(login);
Console.WriteLine(loginLowercased);
This code produces the following output.
LoginResource { Username = Lion-O, Password = jaga, RememberMe = True }
LoginResource { Username = lion-o, Password = jaga, RememberMe = True }
We can go one step further with understanding how with
works. It copies all values from one record to the other. This isn’t a delegation model where one record depends on another. In fact, after the with
operation completes, there is no relationship between the two records. with
only has meaning for record construction. That means for reference types, the copy is just a copy of the reference. For value types, the value is copied.
You can see that semantic at play with the following code.
Console.WriteLine($"Record equality: {login == loginLowercased}");
Console.WriteLine($"Property equality: Username == {login.Username == loginLowercased.Username}; Password == {login.Password == loginLowercased.Password}; RememberMe == {login.RememberMe == loginLowercased.RememberMe}");
It produces the following output.
Record equality: False
Property equality: Username == False; Password == True; RememberMe == True
Record inheritance
It’s easy to extend a record. Let’s assume a new LastLoggedIn
property. It could be added directly to LoginResource
. That’s a fine idea. Records are not brittle like interfaces traditionally have been, unless you want to make new properties required constructor parameters.
In this case, I want to make LastLogin
required. Imagine the codebase is large, and it would be expensive to sprinkle knowledge of the LastLoggedIn
property in all the places where a LoginResource
is created. Instead, we’re going to create a new record that extends LoginResource
with this new property. Existing code will work in terms of LoginResource
and new code will work in terms of a new record that can then assume that the LastLoggedIn
property has been populated. Code that accepts a LoginResource
will happily accept the new record, by virtue of regular inheritance rules.
This new record could be based on any of the LoginResource
variants demonstrated earlier. It will be based on the following one.
public record LoginResource(string Username, string Password)
{
public bool RememberMe {get; init;}
}
The new record could look like the following.
public record LoginWithUserDataResource(string Username, string Password, DateTime LastLoggedIn) : LoginResource(Username, Password)
{
public int DiscountTier {get; init};
public bool FreeShipping {get; init};
}
I’ve made LastLoggedIn
a required property, and taken the opportunity to add additional, optional, properties that may or may not be set. The optional RememberMe
property is also defined, by virtue of extending the LoginResource
record.
Modeling record construction helpers
One of the patterns that isn’t necessarily intuitive is modeling helpers that you want to use as part of record construction. Let’s switch examples, to weight measurements. Weight measurements come from an internet-connected scale. The weight is specified in Kilograms, however, there are some cases where the weight needs to be provided in pounds.
The following record declaration could be used.
public record WeightMeasurement(DateTime Date, double Kilograms) { public double Pounds {get; init;} public static double GetPounds(double kilograms) => kilograms * 2.20462262; }
This is what construction would look like.
var weight = 200; WeightMeasurement measurement = new(DateTime.Now, weight) { Pounds = WeightMeasurement.GetPounds(weight) };
In this example, it is necessary to specify the weight as a local. It isn’t possible to access the Kilograms
property within an object initializer. It is also necessary to define GetPounds
as a static method. It isn’t possible to call instance methods (for the type being constructed) within an object initializer.
Records and Nullability
You get nullability for free with records, right? Everything is immutable, so where would the nulls come from? Not quite. An immutable property can be null and will always be null in that case.
Let’s look at another program without nullability enabled.
using System;
using System.Collections.Generic;
Author author = new(null, null);
Console.WriteLine(author.Name.ToString());
public record Author(string Name, List<Book> Books)
{
public string Website {get; init;}
public string Genre {get; init;}
public List<Author> RelatedAuthors {get; init;}
}
public record Book(string name, int Published, Author author);
This program compiles and will throw a NullReference
exception, due to dereferencing author.Name
, which is null
.
To further drive home this point, the following will not compile. author.Name
is initialized as null
and then cannot be changed, since the property is immutable.
Author author = new(null, null);
author.Name = "Colin Meloy";
I’m going to update my project file to enable nullability.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<LangVersion>preview</LangVersion>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
I’m now seeing a bunch of warnings like the following.
/Users/rich/recordsnullability/Program.cs(8,21): warning CS8618: Non-nullable property 'Website' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. [/Users/rich/recordsnullability/recordsnullability.csproj]
I updated the Author
record with null annotations that describe my intended use of the record.
public record Author(string Name, List<Book> Books)
{
public string? Website {get; init;}
public string? Genre {get; init;}
public List<Author>? RelatedAuthors {get; init;}
}
I’m still getting warnings for the null, null
construction of Author
seen earlier.
/Users/rich/recordsnullability/Program.cs(5,21): warning CS8625: Cannot convert null literal to non-nullable reference type. [/Users/rich/recordsnullability/recordsnullability.csproj]
That’s good, since that’s a scenario I want to protect against. I’ll now show you an updated variant of the program that plays nicely with and enjoys the benefits of nullability.
This program compiles without nullable warnings.
You might be wondering about the following line:
lord.RelatedAuthors.AddRange(
Author.RelatedAuthors
can be null. The compiler can see that the RelatedAuthors
property is set just a few lines earlier, so it knows that RelatedAuthors
reference will be non-null.
However, imagine the program instead looked like the following.
Author GetAuthor()
{
return new Author("Karen Lord")
{
Website = "https://karenlord.wordpress.com/",
RelatedAuthors = new()
};
}
Author lord = GetAuthor();
The compiler doesn’t have the flow analysis smarts to know that RelatedAuthors
will be non-null when type construction is within a separate method. In that case, one of two following patterns would be needed.
lord.RelatedAuthors!.AddRange(
or
if (lord.RelatedAuthors is object)
{
lord.RelatedAuthors.AddRange( ...
}
This is a long demonstration of records nullability just to say that it doesn’t change anything about the experience of using nullable reference types.
Separately, you may have noticed that I moved the Books
property on the Author
record to be an initialized get-only property, instead of being a required parameter in the record constructor. This was driven by there being a circular relationship between Author
and Books
. Immutability and circular references can cause headaches. It is OK in this case, and just means that all Author
objects need to be created before Book
objects. As a result, it isn’t possible to provide a fully initialized set of Book
objects as part of Author
construction. The best we could ever expect as part of Author
construction is an empty List<Book>
. As a result, initializing an empty List<Book>
as part of Author
construction seem like the best choice. There is no rule that all of these properties need to be init
style. I’ve chosen to do that to demonstrate the behavior when you do.
We’re about to transition to talk about JSON serialization. This example, with circular references, relates to the Preserving references in JSON object graphs section coming shortly. JsonSerializer
supports object graphs with circular references, but not with types with parameterized constructors. You can serialize the Author
object to JSON, but not back to an Author
object as it is currently defined. If Author
wasn’t a record
or didn’t have circular references, then both serialization and deserialization would work with JsonSerializer
.
System.Text.Json
System.Text.Json
has been significantly improved in .NET 5.0 to improve performance, reliability, and to make it easier for people to adopt that are familiar with Newtonsoft.Json. It also includes support for deserializing JSON objects to records, the new C# feature covered earlier in this post.
If you are looking at using System.Text.Json
as an alternative to Newtonsoft.Json
, you should check out the migration guide. The guide clarifies the relationship between these two APIs. System.Text.Json
is intended to cover many of the same scenarios as Newtonsoft.Json
, but it’s not intended to be a drop-in replacement for or achieve feature parity with the popular JSON library. We try to maintain a balance between performance and usability, and bias to performance in our design choices.
HttpClient
extension methods
JsonSerializer
extension methods are now exposed on HttpClient
and greatly simplify using these two APIs together. These extension methods remove complexity and take care of a variety of scenarios for you, including handling the content stream and validating the content media type. Steve Gordon does a great job of explaining the benefits in Sending and receiving JSON using HttpClient with System.Net.Http.Json.
The following example deserializes weather forecast JSON data into a Forecast
record, using the new GetFromJsonAsync<T>()
extension method.
This code is compact! It is relying on top-level programs and records from C# 9 and the new GetFromJsonAsync<T>()
extension method. The use of foreach
and await
in such close proximity might be make you wonder if we’re going to add support for streaming JSON objects. Yes, in a future release.
You can try this on your own machine. The following .NET SDK commands will create a weather forecast service using the WebAPI template. It will expose the service at the following URL by default: https://localhost:5001/WeatherForecast
. This is the same URL used in the sample.
rich@thundera ~ % dotnet new webapi -o webapi
rich@thundera ~ % cd webapi
rich@thundera webapi % dotnet run
Make sure you’ve run dotnet dev-certs https --trust
first or the handshake between client and server won’t work. If you’re having trouble, see Trust the ASP.NET Core HTTPS development certificate.
You can then run the previous sample.
rich@thundera ~ % git clone https://gist.github.com/3b41d7496f2d8533b2d88896bd31e764.git weather-forecast
rich@thundera ~ % cd weather-forecast
rich@thundera weather-forecast % dotnet run
9/9/2020 12:09:19 PM; 24C; Chilly
9/10/2020 12:09:19 PM; 54C; Mild
9/11/2020 12:09:19 PM; -2C; Hot
9/12/2020 12:09:19 PM; 24C; Cool
9/13/2020 12:09:19 PM; 45C; Balmy
Improved support for immutable types
There are multiple patterns for defining immutable types. Records are just the newest one. JsonSerializer
now has support for immutable types.
In this example, you’ll see the serialization with an immutable struct.
Note: The JsonConstructor
attribute is required to specify the constructor to use with structs. With classes, if there is only a single constructor, then the attribute is not required. Same with records.
It produces the following output:
rich@thundera jsonserializerimmutabletypes % dotnet run
9/6/2020 11:31:01 AM
-1
31
Scorching
{"date":"2020-09-06T11:31:01.923395-07:00","temperatureC":-1,"temperatureF":31,"summary":"Scorching"}
Support for records
JsonSerializer
support for records is almost the same as what I just showed you for immutable types. The difference I want to show here is deserializing a JSON object to a record that exposes a parameterized constructor and an optional init property.
Here’s the program, including the record definition:
It produces the following output:
rich@thundera jsonserializerrecords % dotnet run
{"Date":"2020-09-12T18:24:47.053821-07:00","TemperatureC":40,"Summary":"Hot!"}
Forecast { Date = 9/12/2020 6:24:47 PM, TemperatureC = 40, Summary = Hot! }
Improved Dictionary<K,V>
support
JsonSerializer
now supports dictionaries with non-string keys. You can see what this looks like in the following sample. With .NET Core 3.0, this code compiles but throws a NotSupportedException
.
It produces the following output.
rich@thundera jsondictionarykeys % dotnet run
{"0":"zero","1":"one","2":"two","3":"three","5":"five","8":"eight","13":"thirteen","21":"twenty one","34":"thirty four","55":"fifty five"}
fifty five
Support for fields
JsonSerializer
now supports fields. This change was contributed by @YohDeadfall. Thanks!
You can see what this looks like in the following sample. With .NET Core 3.0, JsonSerializer
fails to serialize or deserialize with types that use fields. This is a problem for existing types that have fields and cannot be changed. With this change, that’s no longer an issue.
Produces the following output:
rich@thundera jsonserializerfields % dotnet run
9/6/2020 11:31:01 AM
-1
31
Scorching
{"date":"2020-09-06T11:31:01.923395-07:00","temperatureC":-1,"temperatureF":31,"summary":"Scorching"}
Preserving references in JSON object graphs
JsonSerializer
has added support for preserving (circular) references within JSON object graphs. It does this by storing IDs that can be reconstituted when a JSON string is deserialized back to objects.
Performance
JsonSerializer
performance is significantly improved in .NET 5.0. Stephen Toub covered some JsonSerializer
improvements in his Performance Improvements in .NET 5 post. I’ll cover a few more here.
Collections (de)serialization
We made significant improvements for large collections (~1.15x-1.5x on deserialize, ~1.5x-2.4x+ on serialize). You can see these improvements characterized in much more detail dotnet/runtime #2259.
The improvements to List<int>
(de)serialization is particularly impressive, comparing .NET 5.0 to .NET Core 3.1. Those changes are going to be show up as meaningful with high-performance apps.
Method | Mean | Error | StdDev | Median | Min | Max | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|---|---|---|
Deserialize before | 76.40 us | 0.392 us | 0.366 us | 76.37 us | 75.53 us | 76.87 us | 1.2169 | – | – | 8.25 KB |
After ~1.5x faster | 50.05 us | 0.251 us | 0.235 us | 49.94 us | 49.76 us | 50.43 us | 1.3922 | – | – | 8.62 KB |
Serialize before | 29.04 us | 0.213 us | 0.189 us | 29.00 us | 28.70 us | 29.34 us | 1.2620 | – | – | 8.07 KB |
After ~2.4x faster | 12.17 us | 0.205 us | 0.191 us | 12.15 us | 11.97 us | 12.55 us | 1.3187 | – | – | 8.34 KB |
Property lookups — naming convention
One of the most common issues with using JSON is a mismatch of naming conventions with .NET design guidelines. JSON properties are often camelCase and .NET properties and fields are typically PascalCase. The json serializer you use is responsible for bridging between naming conventions. That doesn’t come for free, at least not with .NET Core 3.1. That cost is now negligible with .NET 5.0.
The code that allows for missing properties and case insensitivity has been greatly improved in .NET 5.0. It is ~1.75x faster in some cases.
The following benchmarks for a simple 4-property test class that has property names > 7 bytes.
3.1 performance
| Method | Mean | Error | StdDev | Median | Min | Max | Gen 0 | Gen 1 | Gen 2 | Allocated |
|---------------------------------- |-----------:|--------:|--------:|-----------:|-----------:|-----------:|-------:|------:|------:|----------:|
| CaseSensitive_Matching | 844.2 ns | 4.25 ns | 3.55 ns | 844.2 ns | 838.6 ns | 850.6 ns | 0.0342 | - | - | 224 B |
| CaseInsensitive_Matching | 833.3 ns | 3.84 ns | 3.40 ns | 832.6 ns | 829.4 ns | 841.1 ns | 0.0504 | - | - | 328 B |
| CaseSensitive_NotMatching(Missing)| 1,007.7 ns | 9.40 ns | 8.79 ns | 1,005.1 ns | 997.3 ns | 1,023.3 ns | 0.0722 | - | - | 464 B |
| CaseInsensitive_NotMatching | 1,405.6 ns | 8.35 ns | 7.40 ns | 1,405.1 ns | 1,397.1 ns | 1,423.6 ns | 0.0626 | - | - | 408 B |
5.0 performance
| Method | Mean | Error | StdDev | Median | Min | Max | Gen 0 | Gen 1 | Gen 2 | Allocated |
|---------------------------------- |---------:|--------:|--------:|---------:|---------:|---------:|-------:|------:|------:|----------:|
| CaseSensitive_Matching | 799.2 ns | 4.59 ns | 4.29 ns | 801.0 ns | 790.5 ns | 803.9 ns | 0.0985 | - | - | 632 B |
| CaseInsensitive_Matching | 789.2 ns | 6.62 ns | 5.53 ns | 790.3 ns | 776.0 ns | 794.4 ns | 0.1004 | - | - | 632 B |
| CaseSensitive_NotMatching(Missing)| 479.9 ns | 0.75 ns | 0.59 ns | 479.8 ns | 479.1 ns | 481.0 ns | 0.0059 | - | - | 40 B |
| CaseInsensitive_NotMatching | 783.5 ns | 3.26 ns | 2.89 ns | 783.5 ns | 779.0 ns | 789.2 ns | 0.1004 | - | - | 632 B |
TechEmpower improvement
We’ve spent significant effort improving .NET performance on the TechEmpower benchmark. It made sense to validate these JsonSerializer
improvements with the TechEmpower JSON benchmark. Performance is now ~ 19% better, which should improve the placement of .NET on that benchmark once we update our entries to .NET 5.0. Our goal for the release was to be more competitive with netty
, which is a common Java webserver.
These changes, and the performance measurements are covered in detail at dotnet/runtime #37976. There are two sets of benchmarks there. The first is validating performance with the JsonSerializer
performance benchmarks that the team maintains. There is an ~8% improvement observed. The next section is for TechEmpower. It measures three different approaches for satisfying the requirements of the TechEmpower JSON benchmark. SerializeWithCachedBufferAndWriter
is the one we use in the official benchmark.
Method | Mean | Error | StdDev | Median | Min | Max | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|---|---|---|
SerializeWithCachedBufferAndWriter (before) | 155.3 ns | 1.19 ns | 1.11 ns | 155.5 ns | 153.3 ns | 157.3 ns | 0.0038 | – | – | 24 B |
SerializeWithCachedBufferAndWriter (after) | 130.8 ns | 1.50 ns | 1.40 ns | 130.9 ns | 128.6 ns | 133.0 ns | 0.0037 | – | – | 24 B |
If we look at Min
column, we can do some simple math to calculate the improvement: 153.3/128.6 = ~1.19
. That’s a 19% improvement.
Closing
I hope you’ve enjoyed this deeper dive into records and JsonSerializer
. They are just two of the many improvement in .NET 5.0. The Preview 8 post covers a larger set of features, that provides a broader view of the value that’s coming in 5.0.
As you know, we’re not adding any new features in .NET 5.0 at this point. I’m using these late preview and RC posts to cover all the features we’ve built. Which ones would you like to see me cover in the RC2 release blog post? I’d like to know what I should focus on.
Please share your experience using RC1 in the comments. Thanks to everyone that has installed .NET 5.0. We appreciate all the engagement and feedback we’ve received so far.
As usual Microsoft cant be bothered to answer anyone’s questions : – )
Hi!
Two questions:
– Is WPF a product that will no longer be updated?
– How can you propose a “Azure Web app” that is not even compatible with Azure DevOps but only github? make no sense :'(
Thanks!
Are there any substantive changes to SignalR and MessagePack as used with Blazor WebAssembly and .Net Core?
On one of my machines, running an ASP.NET Core application that I’ve upgraded to .NET 5 RC1 justs exits with code 1 and no output. I’d love to post some stacktrace or memdump but I don’t know how to persuade dotnet to give any output at all.
System.Windows.Forms.OpenFileDialog
Setting a value for the “InitialDirectory” property has no effect when running ShowDialog.
Works fine with .NET Core 3.1.
Hi, thank’s, looks great!!
I have a tiny question.
What version of Visual Studio will I have to use when the final version of .NET 5 is released on November?
Visual Studio 2019 or Visual Studio 2019 Preview?
I mean, Visual Studio 2019 (not preview) will update as well to support .NET 5 ?
Thanks!!
As usual Microsoft cant be bothered to answer anyone’s questions : – )
Hi Sir,
Would like to know about the Blazor client WebAssembly.
Is it possible to develop the reusable components in Blazor and use it in other frameworks like Angular applications or java script web applications.
i.e Using the Blazor components in Angular application.
Could you please let us know if this is possible or not.
Thanks,
Ram.
What about graphics in .NET 5? Can we look forward to getting rid of retained mode graphics?
Hello Microsoft.
Looking forward to .NET 5.0 but we also need to know how to block the installation on those application servers where its not supported.
Is the registry key to block it, “BlockNetFrameWork50” and set the value to “1” ?