Three new LINQ methods in .NET 9

1/27/2024
2 minute read

Even though we are in the alpha of .NET 9 and .NET 8 was released not more than two months ago, the dotnet team does not sleep and pushes new changes! In this blog post, we are checking what new methods were added to everyones favorite: LINQ.

CountBy

Many LINQ functions have a By extension, where you can provide a selector function to group the elements by. For example, MinBy, MaxBy, DistinctBy, and so on. Now we have a new one: CountBy. It groups the elements by the selector function and returns an enumeration of KeyValuePairs. The key is the object, and the value is the count of the elements in the group.

public record Person(string FirstName, string LastName);

List<Person> people =
[
    new("Steve", "Jobs"),
    new("Steve", "Carell"),
    new("Elon", "Musk")
];

foreach (var peopleWithSameFirstName in people.CountBy(p => p.FirstName))
{
    Console.WriteLine($"There are {peopleWithSameFirstName.Value} people with the name {peopleWithSameFirstName.Key}");
}

This prints:

There are 2 people with the name Steve
There are 1 people with the name Elon

AggregateBy

This method is similar to CountBy, but instead of counting the elements, it aggregates them. You can provide a seed and an aggregation function. The aggregation function gets the current aggregation and the current element as parameters. The aggregation function must return the new aggregation.

public record Person(string FirstName, string LastName, int SomeNumber);

List<Person> people =
[
    new("Steve", "Jobs", 10),
    new("Steve", "Carell", 100),
    new("Elon", "Musk", 10)
];

var aggregateBy = people.AggregateBy(person => person.SomeNumber, x => 0, (x, y) => x + y.SomeNumber);
foreach (var kvp in aggregateBy)
{
    Console.WriteLine($"Sum of SomeNumber for Key {kvp.Key} is {kvp.Value}");
}

This will print:

Sum of SomeNumber for key 10 is 20
Sum of SomeNumber for key 100 is 100

Index

Now this isn't necessarily really new, because you could do this as of today. Index does return the item and the index of the item in the collection.

public record Person(string FirstName, string LastName);

List<Person> people =
[
    new("Steve", "Jobs"),
    new("Steve", "Carell"),
    new("Elon", "Musk")
];

foreach (var (index, item) in people.Index())
{
    Console.WriteLine($"Entry {index}: {item}");
}

This will print:

Entry 0: Person { FirstName = Steve, LastName = Jobs }
Entry 1: Person { FirstName = Steve, LastName = Carell }
Entry 2: Person { FirstName = Elon, LastName = Musk }

You could utilize Select overload to do the same:

foreach (var (item, index) in people.Select((item, index) => (item, index)))
{
    Console.WriteLine($"Entry {index}: {item}");
}

What bugs me the most is, that the order of index and item is reversed! But that was a concious decision by the dotnet team:

We decided to return Index first, Value second because it feels more natural despite the fact that the existing Select() method provides value first and index second.

Source: https://github.com/dotnet/runtime/issues/95563#issuecomment-1852656539

More information

LINQ MindMap: .NET 9 Edition

After my first LINQ MindMap: Here is an updated version that includes everything up until .NET 9.

.NET 9 LINQ Performance Edition

As with almost every edition of .NET, the team has been working on improving performance. In this blog post, we will see some improvements to the related tickets and benchmarks.

A new lock type in .NET 9

There is a new sheriff in town when it comes to the lock keyword, And that is the new System.Threading.Lock type that is introduced in .NET 9. And yes, I know - we still need time to digest the big .NET 8 release.

An error has occurred. This application may no longer respond until reloaded. Reload x