Can you believe it? It feels like just yesterday that we were geeking out over .NET 7, and here we are already talking about .NET 8! Time flies when you’re coding up a storm.
But fear not, Microsoft is hard at work bringing us the latest and greatest features that we’ll be exploring in this article.
So sit back, relax, and let’s dive into all the exciting new updates that .NET 8 has in store for us.
dotnet publish and dotnet pack
Microsoft just released an awesome new feature for the dotnet publish
and dotnet pack
commands that makes it even easier to produce production-ready code.
Before this update, these commands produced Debug
assets by default, which could be a bit of a hassle if you wanted to produce production-ready code. But now, with the new update, dotnet publish
and dotnet pack
produce Release
assets by default, which means you can easily produce production-ready code without any extra steps.
But don’t worry, if you still need to produce Debug
assets for any reason, it's still possible to do so by setting the in false
the PublishRelease
property.
How dotnet publish and dotnet pack works?
First, let’s create a new console application using the dotnet new console
command. Then, let's build the project using dotnet build
and take a look at the output. In this case, the output will be in Debug
mode, since that's the default behavior of dotnet build
.
Next, let’s run the dotnet publish
command to produce production-ready code. With the new update, this command will now produce Release
assets by default, which is exactly what we want for production code. We can see the Release
assets in the /app/bin/Release/net8.0
directory.
Finally, let’s say we need to produce Debug
assets for some reason. We can do that by running the dotnet publish
command again, but this time we'll set the PublishRelease
property to false
. This will produce Debug
assets instead of Release
assets, which we can see in the /app/bin/Debug/net8.0
directory.
And that’s it! With this new feature, it’s now easier than ever to produce production-ready code using the dotnet publish
and dotnet pack
commands.
📚Check: dotnet publish and dotnet pack
Improvements in System.Text.Json serialization
System.Text.Json
is a built-in .NET library that provides JSON serialization and deserialization functionality. It allows developers to convert .NET objects to JSON data and vice versa.
Now, System.Text.Json
serialization and deserialization functionality has been improved in various ways for .NET 8.
Another series of improvements included in this preview are in the source generator when used with ASP.NET Core in Native AOT apps, making it more reliable and faster.
Additionally, the source generator will support in .NET 8 serializing types with required
and init properties
, which were already supported in reflection-based serialization.
Moreover, customization of serialization for members that aren’t present in the JSON payload is now possible.
Lastly, properties from interface hierarchies can now be serialized, including those from both the immediately implemented interface and its base interface. Let’s check this example:
IDerived value = new DerivedImplement { Base = 0, Derived =1 };
JsonSerializer.Serialize(value); // {"Base":0,"Derived":1}
public interface IBase
{
public int Base { get; set; }
}
public interface IDerived : IBase
{
public int Derived { get; set; }
}
public class DerivedImplement : IDerived
{
public int Base { get; set; }
public int Derived { get; set; }
}
Now, the JsonNamingPolicy
has been expanded to include naming policies for snake_case
(with an underscore) and kebab-case
(with a hyphen) property name conversions. These new policies can be utilized in the same way as the JsonNamingPolicy.CamelCase
policy.
var options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower };
JsonSerializer.Serialize(new { PropertyName = "value" }, options); // { "property_name" : "value" }}
The JsonSerializerOptions.MakeReadOnly()
method gives you explicit control over when a JsonSerializerOptions
instance is frozen. You can also check whether it's read-only with the IsReadOnly
property.
📚Check: System.Text.Json serialization
GetItems()
The GetItems<T>()
method is a new feature in .NET 8 that allows you to randomly select a specific number of items from a given set of elements.
This can be useful in games, simulations, and other applications where randomness is desired. The method is available in both System.Random
and System.Security.Cryptography.RandomNumberGenerator
.
In this example, we have an array of City
objects, and we use the GetItems<T>()
method to randomly select 3 cities from the array:
private static ReadOnlySpan<City> s_allCities = new[] { new City("New York", "USA"), new City("London", "UK"), new City("Paris", "France"), new City("Tokyo", "Japan"), new City("Sydney", "Australia"), };...City[] selectedCities = Random.Shared.GetItems(s_allCities, 3); foreach (City city in selectedCities) { Console.WriteLine(city.Name + ", " + city.Country); } // Output: // Paris, France // Tokyo, Japan // Sydney, Australia
We then loop through the selected cities and print their name and country to the console. The output will be different each time we run the program, because the cities are selected randomly.
📚Check: GetItems()
Shuffle()
The Shuffle<T>()
method is another new feature in .NET 8 that allows you to randomize the order of elements in a span.
This is important in machine learning instances when you wish to eliminate training bias by randomizing training data order.
Here’s an example that shows how to use Shuffle<T>()
with an array of YourType
objects:
YourType[] trainingData = LoadTrainingData(); Random.Shared.Shuffle(trainingData);IDataView sourceData = mlContext.Data.LoadFromEnumerable(trainingData);DataOperationsCatalog.TrainTestData split = mlContext.Data.TrainTestSplit(sourceData); model = chain.Fit(split.TrainSet);IDataView predictions = model.Transform(split.TestSet); // ...
In this example, we load some training data into an array of YourType
objects and use Random.Shared
to shuffle the order of the elements.
We then load the shuffled data into an IDataView
object, split the data into training and test sets, and use the shuffled training data to train a machine learning model. Finally, we use the trained model to make predictions on the test set.
📚Check: Shuffle()
Performance Improvements
.NET 8 introduces several new types that are focused on improving app performance. These new types are:
- The
System.Collections.Frozen
namespace includes two collection types,FrozenDictionary<TKey,TValue>
andFrozenSet<T>
. These types do not allow any changes to keys and values once a collection is created, which allows for faster read operations such asTryGetValue()
. They are particularly useful for collections that are populated on first use and then persisted for the duration of a long-lived service:
private static readonly FrozenDictionary<string, bool> s_configurationData = LoadConfigurationData().ToFrozenDictionary(optimizeForReads: true); // ... if (s_configurationData.TryGetValue(key, out bool setting) && setting) { Process(); }
- The
System.Text.CompositeFormat
type is useful for optimizing format strings that aren't known at compile time. A little extra time is spent up front to do work such as parsing the string, but it saves the work from being done on each use:
private static readonly CompositeFormat s_rangeMessage = CompositeFormat.Parse(LoadRangeMessageResource()); // ... static string GetMessage(int min, int max) => string.Format(CultureInfo.InvariantCulture, s_rangeMessage, min, max);
The System.Buffers.IndexOfAnyValues<T>
type is designed to be passed to methods that look for the first occurrence of any value in the passed collection. .NET 8 adds new overloads of methods like String.IndexOfAny
and MemoryExtensions.IndexOfAny
that accept an instance of the new type.
📚Check: Performance-focused types
Native AOT
.NET 8 brings improvements to the native ahead-of-time (AOT) compilation feature that was first introduced in .NET 7. Publishing an application as native AOT generates a self-contained version of the app that doesn’t require a runtime as everything is included in a single file.
In addition to the existing support for various platforms, .NET 8 now includes support for the x64 and Arm64 architectures on macOS. This means that developers can now publish their .NET apps as native AOT for macOS systems.
The latest improvements to native AOT apps on Linux systems have resulted in significantly reduced application sizes.
According to recent tests, native AOT apps built with .NET 8 Preview 1 now take up to 50% less space compared to those built with .NET 7.
You can see in the table below a comparison of the size of a “Hello World” app published with native AOT and including the entire .NET runtime between the two versions:
Operating System | .NET 7 | .NET 8 Preview 1 |
---|---|---|
Linux x64 (with -p:StripSymbols=true) | 3.76 MB | 1.84 MB |
Windows x64 | 2.85 MB | 1.77 MB |
These improvements in native AOT can help .NET developers to create smaller, faster, and more efficient apps that run on a variety of platforms without requiring any external dependencies.
📚Check: Native AOT
Code generation
.NET 8 also has some improvements in code generation and JIT (Just-In-Time) compilation enhancing performance and efficiency:
- Arm64 architecture performance improvements
- SIMD (Single Instruction Multiple Data) improvements for better vectorization and parallelization of operations
- Cloud-native improvements for better performance in containerized environments
- Profile-guided optimization (PGO) improvements that enable better optimizations based on application usage patterns
- Support for AVX-512 ISA extensions for more efficient floating-point operations on modern CPUs
- JIT (Just-In-Time) t*hroughput improvements* for faster code generation
- Loop and general optimizations that improve the performance of frequently used code blocks
These improvements help developers to optimize the performance of their .NET applications and reduce resource utilization in cloud-native environments.
📚Check: Code generation
.NET container images
.NET 8 also makes a few changes to the way .NET container images work. First, Debian 12 (Bookworm) is now the default Linux distribution in the container images.
Additionally, the images include a non-root
user to make the images non-root
capable. To run as non-root
, add the line USER app
at the end of your Dockerfile or a similar instruction in your Kubernetes manifests.
The default port has also changed from 80
to 8080
and a new environment variable ASPNETCORE_HTTP_PORTS
is available to make it easier to change ports.
The format for the ASPNETCORE_HTTP_PORTS
variable is easier compared to the format required by ASPNETCORE_URLS
, and it accepts a list of ports. If you change the port back to 80
using one of these variables, it won't be possible to run as non-root
.
To pull the .NET 8 Preview SDK, you can use the following tag which includes the -preview
suffix in the tag name for preview container images:
docker run --rm -it mcr.microsoft.com/dotnet/sdk:8.0-preview
The suffix -preview
will no longer be used for release candidate (RC) releases. In addition, developers can use chiseled Ubuntu images with .NET 8 that offer a smaller attack surface, no package manager or shell, and non-root
capability. These images are ideal for developers looking for the advantages of appliance-style computing.
📚Check: .NET container images
So, are you excited to try out these new features in .NET 8? What do you think will be the most useful addition for your projects? Don’t hesitate to download the preview and explore the possibilities. Happy coding!
Top comments (4)
Those are looking very promising! But I will probably stick with Newtonsoft's Json for a long time.
Thanks for the article 🎉
We are also fans of Newtonsoft's Json❤️
The Shuffle portion looks interesting. Totally could have used that in college with the universal blackjack assignment app 😅
It's always great to see how new features can be applied to solve real-world problems. Hope the Shuffle feature can be useful to you in future projects as well!