Microsoft released .NET Core 1.0 in June 2016, then 1.1 in November 2016, followed by 2.0 in August 2017 and 2.1 in May 2018. The latest final release was .NET Core 2.2 in December 2018. Now, the next major release will be .NET Core 3.0 in the second half of 2019. A more specific release date will be announced in May at the Build 2019 conference.

Luckily, we don't have to wait that long, because there are already preview versions available! Moreover, the first preview of .NET Core 3.0 and the final version of .NET Core 2.2 were released at the same time in December 2018. This article is based on .NET Core Preview3 and will discuss the known new features according to release notes.

Desktop development: Forms and WPF

Are you kiddin' me? The old school Windows Forms in a brand new release of the open-source .NET Core development platform? No, I am serious: this is .NET Core's next big thing. So, does this mean .NET Core will not be cross-platform anymore, or does this mean Windows Forms and WPF are ported to platforms like Linux and Mac? Well, it is even more confusing, because both are not true. .NET Core will stay cross-platform, but the Forms and WPF features are only supported on Windows. The new desktop application libraries will be available as Windows Desktop Packs. This set of libraries will have full access to the Windows 10 APIs. Next to the Windows 10 APIs, in .NET Core 3.0 it is even possible to CoCreate COM APIs and Activate WinRT APIs. So you can now completely integrate with the Windows ecosystem.

But you can already share code between .NET Framework and .NET Core by using .NET Standard libraries, so why should we put effort in converting desktop applications to .NET Core?

The biggest advantages of using .NET Core for desktop applications:
1. use a specific version of .NET Core (side-by-side and app-local).
2. performance.

Want to know more? Richard Lander (Principal Program Manager of .NET Core) wrote a nice article about desktop applications in .NET Core 3.0.

P.S. Windows Forms and WPF are open-source now!

Local dotnet tools

Since .NET Core 2.1 it is already possible to install global tools for dotnet. Now it also possible to install tools per project. To achieve this, there is a new file which you will probably see a lot in .NET Core 3.0 projects: dotnet-tools.json. This file allows you to specify exactly which version of what tool should be used in the project. Running the tools can be done with: dotnet tool run <toolCommandName>, which is really easy to use in automated build scripts.

Using this dotnet-tools.json file will save a lot of frustration when running a project on different computers. Downside: tools might require a specific .NET Core version, which you still have to install.

Executables and dependencies at build

When building a .NET Core 3.0 application, an executable of your application will also be created. In previous versions, it was already possible to create .exe files, but only for self-contained applications. Having an executable file is really usefull for distribution, because running dotnet app.dll was't that nice.

Besides, used dependencies (NuGet packages) are now also added to the build folder. Therefore it is possible to grab the build output and run it on a different machine, which can be really useful to quickly test applications in different environments.

Hardware communication: Serial, GPIO, PWM, SPI and I2C

Internet of Things (IoT) is becoming more and more popular. Microsoft understands that, as a cross-platform platform, .NET Core can add a lot of value to IoT, but good communication with hardware components is essential. Therefore, new APIs were built for GPIO, PWM, SPI and I2C, but also support for serial ports in Linux is added.

Also worth mentioning: ARM64 for Linux is now supported, allowing you to run 64-bit applications on a Raspberry Pi 3.

Range and Index

The new features above are pretty huge, but if you don't build desktop applications, work with IoT, or care about deployment, it might sounds like there is no reason for you to upgrade. Don't stop reading, because the following C# 8 feature will be interesting for all of us!

With the new Range feature it is easy to select a part of an Array:

int[] a = { 10, 20, 30, 40, 50 };
return a[0..3]; // [10, 20, 30]

Next to the Range, there is also a new Index feature allowing you to define the index of the ranges easily:

int[] a = { 10, 20, 30, 40, 50 };
Index i1 = 0; // 0 from the beginning: 10
Index i2 = ^2; // 2 from the end: 40 
return a[i1..i2]; // [10, 20, 30]

Async streams

Other fantastic news about .NET Core, is the introduction of async streams. These async streams allow you to wait for results of async tasks while already processing the first responses.

async IAsyncEnumerable<string> GetTaxReportsAsync()
    foreach(var request in GetTaxReportRequests())
    	var result = await GetTaxReportAsync(request);
    	if (result.Success)
            yield return result.TaxReport;

In the example above, a batch of tax reports is generated based on a list of requests. Every time a new report is generated, it returns it to the user.


Till now, Json.NET was the most popular JSON libary for .NET. However, this library is based on UTF16 encoded string, which is not always preferable. Therefore .NET Core 3.0 brings you the Utf8JsonReader and Utf8JsonWriter. Microsoft even promised a 100% performance increase.

Another way to parse JSON files, is to use the new JsonDocument, which is build on top of the new UTF8 JSON library. This parser works quite the same as the XmlDocument:

using (JsonDocument doc = JsonDocument.Parse(json))
    JsonElement root = doc.RootElement;
    JsonElement person = root[1];
    string firstName = person[0].GetString();

Unload assemblies

Loading assemblies at runtime is really important if you want to support plug-ins in your application. Fortunately this is already supported by .NET for a long time, so you can load added .dll files at runtime. However, the new .NET Core 3.0 can not only load assemblies, but also has an option to unload! This is a major improvement, because it allows you to free memory if plug-ins are not used anymore, or install new versions of already loaded plug-ins.

With AssemblyLoadContext.LoadFromAssemblyPath and AssemblyLoadContext.Unload the assemblies can be loaded and unloaded easily.


The new MetadataLoadContext looks a lot like the good old assembly loading, but there is a big difference! While it was only possible to inspect a .dll file by loading the whole assembly and then enumerate the types. With .NET Core 3.0, Microsoft allows us to only load the metadata, without having all those classes in your AppDomain yet.

var paths = new string[] {@"plugin.a.dll", @"plugin.b.dll"};
var resolver = new PathAssemblyResolver(paths);
var pluginInterface = typeof(IPlugin);
using (var context = new MetadataLoadContext(resolver))
    var assembly = context.LoadFromAssemblyName("plugin.a");
    foreach (var type in assembly.GetTypes())
        if (type.IsClass && pluginInterface.IsAssignableFrom(type))
            Console.WriteLine($"Plugin found: {t.FullName}");


In .NET Core 3.0, System.Buffers.SequenceReader was added to be able to read ReadOnlySequence<T> objects. There are no real world examples at this moment using the SequenceReader, so it is a bit unclear when this feature can be used best, but it might be interesting to process a stream of bytes from a camera or bar code scanner.

public static void ReadLines(ReadOnlySequence<byte> sequence)
    var reader = new SequenceReader<byte>(sequence);

    while (!reader.End)

Default implementation of interfaces

C# has support for interfaces and abstract classes since the first release. But now, in C# 8, Microsoft introduced a new feature covering both! It is possible to add a default implementation for methods of an interface. It sounds crazy, but the following example is correct:

interface IBook
    public void Print()
        Console.WriteLine("This book has no content.");

So no need to use abstract classes anymore? Maybe, but please be careful with this new feature, as it is really easy to use it as a shortcut not having to create a separate service class. For instance, it is much better to add a BookPrinter class to your project having a Print(IBook book) method, than extending you current classes with interfaces containing all kind of logic.

The development and release of .NET Core 3.0 goes in parallel with ASP.NET Core and Entity Framework Core. Besides, .NET Core is also tightly related to .NET Standard, C# 8 and F# 4.6. And last but not least, new Docker containers are available for each preview release.

More information