New .NET 6 APIs driven by the developer community

David Fowler

.NET 6 is on the way, and I wanted to share some of my favorite new APIs in .NET and ASP.NET Core that you are going to love. Why are you going to love them? Well because they were directly driven by our fantastic .NET developer community! Let’s get started!

Reading & Writing Files

In .NET 6, there’s a new low-level API to enable reading/writing of files without using a FileStream. It also supports scatter/gather IO (multiple buffers) and overlapping reads and writes at a given file offset.

using Microsoft.Win32.SafeHandles;
using SafeFileHandle handle = File.OpenHandle("ConsoleApp128.exe");
long length = RandomAccess.GetLength(handle);

Console.WriteLine(length);

Process Path & Id

There are a couple of new ways to access a process path and process id without allocating a new process object:

int pid = Environment.ProcessId;
string path = Environment.ProcessPath;

Console.WriteLine(pid);
Console.WriteLine(path);

CSPNG (Cryptographically Secure Pseudorandom Number Generator)

Generating random numbers from a CSPNG (Cryptographically Secure Pseudorandom Number Generator) is a easier than ever:

// Give me 200 random bytes
byte[] bytes = RandomNumberGenerator.GetBytes(200);

Parallel.ForEachAsync

We finally added Parallel.ForEachAsync, a way to schedule asynchronous work that allows you to control the degree of parallelism:

var urlsToDownload = new [] 
{
    "https://dotnet.microsoft.com",
    "https://www.microsoft.com",
    "https://twitter.com/davidfowl"
};

var client = new HttpClient();

await Parallel.ForEachAsync(urlsToDownload, async (url, token) =>
{
    var targetPath = Path.Combine(Path.GetTempPath(), "http_cache", url);

    HttpResponseMessage response = await client.GetAsync(url);

    if (response.IsSuccessStatusCode)
    {
        using FileStream target = File.OpenWrite(targetPath);

        await response.Content.CopyToAsync(target);
    }
});

Configuration Helpers

We added a helper to make it easier to throw if a required section of configuration is missing:

var configuration = new ConfigurationManager();
var options = new MyOptions();

// This will throw if the section isn't configured
configuration.GetRequiredSection("MyOptions").Bind(options);

class MyOptions
{
    public string? SettingValue { get; set;}
}

LINQ

There’s a ton of new LINQ methods as well. It got lots of love in this release. Here’s a new helper to chunk any IEnumerable into batches:

int chunkNumber = 1;
foreach (int[] chunk in Enumerable.Range(0, 9).Chunk(3))
{
    Console.WriteLine($"Chunk {chunkNumber++}");
    foreach (var item in chunk)
    {
        Console.WriteLine(item);
    }
}

Even More LINQ!

More LINQ! There are now MaxBy and MinBy methods:

var people = GetPeople();

var oldest = people.MaxBy(p => p.Age);
var youngest = people.MinBy(p => p.Age);

Console.WriteLine($"The oldest person is {oldest.Age}");
Console.WriteLine($"The youngest person is {youngest.Age}");

public record Person(string Name, int Age);

Power of 2

Don’t keep bit math in your head? Me neither. Here are some new helpers for working with powers of 2:

using System.Numerics;

uint bufferSize = 235;
if (!BitOperations.IsPow2(bufferSize))
{
    bufferSize = BitOperations.RoundUpToPowerOf2(bufferSize);
}

Console.WriteLine(bufferSize);

WaitAsync Improvements

There’s now a much easier (and properly implemented) way to wait for task to complete asynchronously. The following code will yield the await if it hasn’t completed in 10 seconds. The operation might still be running! This is for un-cancellable operations!

Task operationTask = SomeLongRunningOperationAsync();

await operationTask.WaitAsync(TimeSpan.FromSeconds(10));

ThrowIfNull

No more having to check for null in every method before you throw an exception. It is now just one line of code.

void DoSomethingUseful(object obj)
{
    ArgumentNullException.ThrowIfNull(obj);
}

Working with NativeMemory

If you want to use C APIs to allocate memory because you’re a l33t hacker or need to allocate native memory, then look no further. Don’t forget to free!

using System.Runtime.InteropServices;

unsafe
{
    byte* buffer = (byte*)NativeMemory.Alloc(100);

    NativeMemory.Free(buffer);
}

Posix Signal Handling

Native support for Posix signal handling is here and we also emulate a couple of signals on Windows.

using System.Runtime.InteropServices;

var tcs = new TaskCompletionSource();

PosixSignalRegistration.Create(PosixSignal.SIGTERM, context =>
{
    Console.WriteLine($"{context.Signal} fired");
    tcs.TrySetResult();
});

await tcs.Task;

New Metrics API

We added an entirely new metrics API based on @opentelemetry in .NET 6. It supports dimensions, is super efficient and will have exporters for popular metric sinks.

using System.Diagnostics.Metrics;

// This is how you produce metrics

var meter = new Meter("Microsoft.AspNetCore", "v1.0");
Counter<int> counter = meter.CreateCounter<int>("Requests");

var app = WebApplication.Create(args);

app.Use((context, next) =>
{
    counter.Add(1, KeyValuePair.Create<string, object?>("path", context.Request.Path.ToString()));
    return next(context);
});

app.MapGet("/", () => "Hello World");

You can even listen in and meter:

var listener = new MeterListener();
listener.InstrumentPublished = (instrument, meterListener) =>
{
    if(instrument.Name == "Requests" && instrument.Meter.Name == "Microsoft.AspNetCore")
    {
        meterListener.EnableMeasurementEvents(instrument, null);
    }
};

listener.SetMeasurementEventCallback<int>((instrument, measurement, tags, state) =>
{
    Console.WriteLine($"Instrument: {instrument.Name} has recorded the measurement: {measurement}");
});

listener.Start();

Modern Timer API

Last but not least, a modern timer API (I think this is the 5th timer API in .NET now). It’s fully async and isn’t plagued by the types of gotchas the other timers are plagued with like object lifetime issues, no asynchronous callbacks etc.

var timer = new PeriodicTimer(TimeSpan.FromSeconds(1));

while (await timer.WaitForNextTickAsync())
{
    Console.WriteLine(DateTime.UtcNow);
}

Summary

This is just a sampling of the new APIs coming in .NET 6. For more information look at the .NET 6 release notes API diffs. Also, Stephen just wrote a spectacular blog on performance improvements in .NET6 so be sure to give that a read. Finally, don’t forget to download the .NET 6 Preview and try out the new APIs today.

59 comments

Discussion is closed. Login to edit/delete existing comments.

  • dmitry aph 0

    Love the new api capabilities – less code for more features. Btw there is a typo in

    white(await timer.WaitForNextTickAsync())
    

    should be while instead of white or it’s a new keyword that I don’t know yet 🙂

    • Chris Heil 0

      also formatting the link on the first line is broken

      .NET 6 is on [the way](https://devblogs.microsoft.com/dotnet/announcing-net-6-preview-7/), and I [...]
      • textand fontz 0

        Thanks for your comment. I was really confused in following linke

        .NET 6 is on [the way](https://devblogs.microsoft.com/dotnet/announcing-net-6-preview-textandfonts/), and I [...]
  • Reilly Wood 0

    Some very handy new APIs here, thanks!

    One question: is

    NativeMemory.Alloc

    any different from the venerable

    Marshal.AllocHGlobal

    ?

    • Tanner Gooding Microsoft employee 0

      Marshal.AllocHGlobal is documented (and won’t change due to back-compat concerns) to call LocalAlloc on Win32. The LocalAlloc API is a legacy API that isn’t recommended for use in modern applications.

      NativeMemory.Alloc on the other hand is documented to be a relatively thin wrapper over malloc. It should therefore be faster and compatible with standard memory allocation/free logic in modern apps. The other functions in NativeMemory are likewise documented, in the remarks section of each method, to be thin wrappers over particular APIs. Noting that certain APIs such as AlignedAlloc can’t be guaranteed to map to something like aligned_alloc as that may not be available on all supported platforms. In those cases, the remarks section calls out that it may be a thin wrapper over different APIs and you should use a corresponding function for freeing or reallocation.

      • switchdesktopwithfade@hotmail.com 0

        Why doesn’t NativeMemory.Alloc return a SafeHandle?

        • Tanner Gooding Microsoft employee 0

          While SafeHandle is the recommended way for most code to safely manage and dispose native handles, it is not free and comes with additional overhead (such as being a secondary GC tracked allocation) that can make it undesirable for a number of low-level or performance oriented scenarios.

          NativeMemory exposes what are effectively raw/primitive allocation APIs that can be used in such performance oriented scenarios or which can be used to build more complex features and scenarios, including those that involve SafeHandle or other helper types appropriate for a given use case.

          • switchdesktopwithfade@hotmail.com 0

            Are NativeMemory.Alloc and Marshal.AllocCoTaskMem both somehow protected from the async thread abort problem even though they don’t return SafeHandles? That’s the only gray area of confusion that keeps me from using either.

  • MgSam 0

    I have to agree with @stephentoub’s comment on his own PR that using IAsyncEnumerable for this timer use case would have been a lot nicer than adding yet-another-timer. Unless you guys [Obsolete] the old timers, there is a 100% chance people will still have no idea what timer to use or why. Other than the maybe few dozen people who followed the Github issue and subsequent PR. Which really begs the question why none of those 4 existing timers haven’t been deprecated already?

    • David FowlerMicrosoft employee 0

      My gut says that we’ll eventually have PeriodicTimer implement IAsyncEnumerable, it’s probably the best of all worlds. As for deprecation, I think it’s possible now that we have this timer, but yes, we should re-evaluate for sure.

  • Jack Bond 0

    In articles like this, and code as well, it’s annoying as **** when developers write code like this…

    using var handle = File.OpenHandle(“ConsoleApp128.exe”);

    Just great. What does File.OpenHandle return? It used to be plain as day. Now I need to hover over the variable, and that’s assuming I’m in Visual Studio, to find out. What do I do in an article like this, guess?

    Here’s a real simple rule, only use var when working with anonymous types.

    • Lachlan Picking 0

      I used to follow this rule, but in recent years I’ve relaxed a fair bit on it, especially after talking to other developers. You don’t always need to know the exact type, you just need enough context to know roughly what the object represents. In this case,

      using var handle

      tells me that it’s some disposable type that probably wraps a file handle. I think that’s all I really need to know.

      • David FowlerMicrosoft employee 0

        I shall oblige the original poster for asking so nicely 🙂

      • Jack Bond 0

        “other developers” translates to me as “lazy hacks”

        I don’t always need to know the exact type, but when I do, some lazy hack has wasted my time. And yes, I make the change and check it in.

        Seriously, look at this line…

        var length = RandomAccess.GetLength(handle);

        FFS, it doesn’t even save keystrokes(assuming it’s an int, but who knows, maybe it’s a long). I type var all time, but then “Use explicit type instead of ‘var'”. With all the AI they’re building into VS, it should literally be doing that on the fly.

        • Dan MoseleyMicrosoft employee 0

          For what it’s worth, in dotnet/runtime the coding style is:

          We only use var when the type is explicitly named on the right-hand side, typically due to either new or an explicit cast, e.g. var stream = new FileStream(…) not var stream = OpenStandardInput().

      • schbaem 0

        I fully support @Jack Bond here. In my opinion it is a disgrace to all other developers to write code like this in a strongly-typed language.

        • David FowlerMicrosoft employee 0

          But…var is strongly typed…………

          • Jack Bond 0

            That it is strongly typed is completely irrelevant.

            What’s the most costly part of software development?

            We all know it’s maintenance. Not using var means the code is more maintainable, it just is. And believe me, I’m not religious about this. I noticed you updated the code to not use var (thank you so much), except in cases like this…

            var client = new HttpClient();

            I’m actually OK with that, because the type is plain as day right there. I am glad that the C# guys decided to let you do this…

            HttpClient client = new();

            Now if they’d just give us async constructors.

            Thanks again for updating the code, it really does make the code more readable / self documenting.

          • schbaem 0

            I did not say that var is not strongly-typed, so your comment does not make any sense.

          • Jiří Zídek 0

            Using var saved me so much time while refactoring! Thanks god!

        • Patrick Morris 0

          I used to think var was evil and I loved spelling out the type every time with religious fanaticism. However I started using var a little and now I always use it.

          The code is cleaner, easier to refactor, and more maintainable.

          • Gustavo Sampaio 0

            I agree with that. It’s better to have less clutter and things to look at in the screen, even if that means I have to hover over (or place the cursor on) the variable to find out what type it is. At least for me it’s sort of an ‘attention offsetting’ mechanism. In other words, caring less about the specifics of a variable and more about the problem/rationale behind the code. I think in America they call it a ‘double-take’.

          • Gerard Howell 0

            I also completely agree with this approach.

            Using var rather than explicit types allows you to focus more on the actual purpose of the code, as well as allowing for faster refactoring. And on the rare occasions where the type is unclear from first glance, it only takes a fraction of a second to hover over the variable in an IDE.

    • Vincent Thorn 0

      You’re talking total noncense – push personal limited mind as a rule. Relax! ‘var’ is very convenient sugar, it helps to reduce code a lot. If you see own code first time, it’s not problem of other developers!

      • Jack Bond 0

        Reduce code? Ok, we’ve now received the input from the entry level programmers. Anyone else?

        • Chris Baker 0

          Seems a bit harsh. I don’t consider myself an entry level programmer but I like the use of var. I also don’t have a problem reading mine or other peoples code. I would agree that there are perhaps examples where it may be a little harder to ascertain the return type of a method without have the type explicitly stated. But that would a very small percentage of the time. The majority of the time the code and the use of var is obvious.

          Your example: var length = RandomAccess.GetLength(handle); may return a long an int a uint a ulong who knows when you read it out of context or in an article, does it really matter? I would say not (obviously it matters in the code, but the code knows what it is) and when I change the return type of that method I don’t have to go around fixing up the type at all the call sites. Something like the following:
          string userName = userClass.GetUserName(userId) compared to var userName = userClass.GetUserName(userId), the former adds no value in my opinion (and that’s just my opinion).

          As I said, personally, I like the use of var, but at the same time I don’t force my opinion on others. This has always been a team decision where I have worked and most fall on the side of var, but much of the time these decisions are left up to the individual. I do like consistency though.

          Oh, and by the way, in my example above, what’s the type for the variable userId?

          • Gustavo Sampaio 0

            I agree with that. It’s better to have less clutter and things to look at in the screen, even if that means I have to hover over (or place the cursor on) the variable to find out what type it is. At least for me it’s sort of an ‘attention offsetting’ mechanism. In other words, caring less about the specifics of a variable and more about the problem/rationale behind the code. I think in America they call it a ‘double-take’. Moreover, if keeping track of variable types is such a critical necessity, it’s possible that your methods are bigger than they should be, therefore having to memorize five, six or more variables at once. If your variables live within a very limited context, var should be fine most of the time.

        • Patrick Morris 0

          It does literally reduce code and makes the code easier to read.
          Also, why are you attacking someone for a different opinion?

          Seems like VS could easily show the types for one person and use var for another.

    • Reelix 0

      var carInfo = CustomCarClass.GetInfo(2); // Good luck figuring this one out 🙂

      • Tom Bruyneel 0

        Clearly carInfo is an instance of Button … or it might be of CarInfo, difficult to be sure :p

    • Jorge Morales Vidal 1

      There are manners when asking for changing a piece of code. We are developers but also people and, even though you have a point, we should treat each other with respect. I saw your initial comment and the other one about “entry level developers” as disrespectful. Please be polite when trying to get your ideas across.

  • Gustavo Sampaio 0

    I’m curious about the new LINQ stuff. ThrowIfNull will come in handy too. Do you have plans to do a community standup on these features anytime soon? I always find them quite interesting.

    • David FowlerMicrosoft employee 0

      Hmm, that’s a good idea. I’ll see if we can get one scheduled.

      • Gustavo Sampaio 0

        Thanks, I appreciate it 🙂 Just finished part 2 of the one on ASP.NET Core architecture. It’s fascinating how so many complicated things are working together under the hood to provide us with a pleasing development experience. Keep up with the great work.

  • Denis Balan 1

    In Parallel.ForEachAsync
    combining path with raw url and saving some bytes into newly generated path would throw exception, because of illegal characters in file name (like colon, slash, etc..)

        var targetPath = Path.Combine(Path.GetTempPath(), "http_cache", url);
  • Vincent Thorn 0

    LINQ MaxBy – this thing you MUST do long time ago! Isn’t it so rare function that you ignore it for years???

    • NN 0

      If you need it now you can use CodeJam library.

      • Reelix 0

        If I wanted to be completely reliant on third-party libraries I’d be using node/python instead of .NET :p

  • Vincent Thorn 0

    In the section ‘Reading & Writing Files’ I totally miss what’s helpful in this mess. Where is “reading & writing”?? How it helps me above existing “File.WriteAllBytes”?
    Random access – it was existing before, what new functions are introduced and how they improve old code? Any sample?

    • Dan MoseleyMicrosoft employee 0

      See the original proposal here

      In short, it enables scenarios that need the highest performance, lowest allocation thread safe file access. For most people, the existing API remain good choices.

  • Rand Random 0

    I think this is the 5th timer API in .NET now

    Had the same thought, when I read the title Modern Timer API but still, why didn’t you mark the “old” timers obsolete?

    May I ask why you phrased it as “We added..”, IMHO “We” seems to imply that Microsoft Team did it, which seems condraditing the first paragrah that wanted to highlight the community driven APIs.

    • David FowlerMicrosoft employee 0

      The greater “we”. “We the .NET people”. On a more serious note, APIs that are suggested by the community aren’t always implemented by the community.

  • Peter Wong 0

    Is ThrowIfNull any different from the C# 9 simplified parameter null validation?

    void DoSomethingUseful(object obj!)
    {
        ...
    }
    • Huo Yaoyuan 0

      The language feature has been delayed again and won’t ship with C# 10.

      Once implemented, the language feature will likely generate calls to ThrowIfNull. Calling it explicitly will give you more control then (For example when null is acceptable in some states).

Feedback usabilla icon