This tech update will be very techy, readers beware! Earlier this month .NET 6 was released, offering a boatload of new features. I picked some personal favorite features to dive into, mostly from C# 10. There are way too many new features to highlight in this blog, so I included a link to Microsoft’s announcement blog and a .NET deep dive video by Microsoft below.
Index operator support for ElementAt
Consider the following collection:
var collection = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
// To get the 4th index in this collection
var fourth = collection.ElementAt(4);
// To get the 4th index from the end in this collection, you can now use the index operator ^
var fourthFromEnd = collection.ElementAt(^4);
Read more about index operators and ranges here.
Multiple enumeration
public static bool TryGetNonEnumeratedCount<TSource> (this System.Collections.Generic.IEnumerable<TSource> source, out int count);
Consider the following block of code:
var collection1 = new [] { “foo” };
var collection2 = new [] { “bar” };
IEnumerable<string> collections = collection1.Concat(collection2);
var count = collections.Count();
var orderCollections = collections.OrderBy(c => c);
When doing that count, you enumerate through the collection to count the number of elements in the collection. You then follow up with a sorting algorithm to order the elements, effectively enumerating through the collection again. This is not effective, but .NET 6 gives you a way to optimize this.
When in need of a count, you now can do the following:
var count = 0;
if (collections.TryGetNonEnumeratedCount(out count))
{
//do something
}
Now we don’t have forced enumeration, which can save resources in certain situations.
Enumarable.Chunk
public static System.Collections.Generic.IEnumerable<TSource[]> Chunk (this System.Collections.Generic.IEnumerable source, int size);
Previously, when you wanted to divide a collection into chunks, you had to create your own function to do so. It would look something like this.
var collection = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var chunkie = ChunkMe<int>(collection, 3);
// { 1, 2, 3}
// { 4, 5, 6}
// { 7, 8, 9}
IEnumerable ChunkMe(IEnumerable source, int chunkSize)
{
return source
.Select((x, i) => new { Index = i, Value = x })
.GroupBy(x => x.Index / chunkSize)
.Select(x => x.Select(y => y.Value));
}
In .NET 6 you simple call the Chunk() function on a collection.
var collection = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var chunkie = collection.Chunk(3);
// { 1, 2, 3}
// { 4, 5, 6}
// { 7, 8, 9}
DateOnly and TimeOnly Types
public struct DateOnly : IComparable, IComparable, IEquatable, ISpanFormattable
public struct TimeOnly : IComparable, IComparable, IEquatable, ISpanFormattable
Since forever we have been using the DateTime type in .NET. This is usually fine, but now and then you see these gremlins creep in when trying to get a date or a time from a DateTime. DateOnly and TimeOnly should prevent that when you are looking for just a time or a date.
DateOnly date = DateOnly.MinValue;
Console.WriteLine(date);
// Output: 01-01-0001 (no time indication)
Where DateOnly is pretty straightforward, TimeOnly has some pretty cool features.
TimeOnly time = TimeOnly.MinValue;
Console.WriteLine(time);
// Output: 00:00
Use AddHours() to add time:
TimeOnly time2 = time.AddHours(3);
Console.WriteLine(time2);
// Output: 03:00
IsBetween is another nifty feature:
TimeOnly start = TimeOnly.Parse(“14:00”);
TimeOnly end = TimeOnly.Parse(“18:00”);
Console.WriteLine(TimeOnly.Parse(“16:00”).IsBetween(start, end));
// Output: True
MinBy, MaxBy, By..By..By..
public static TSource? MinBy<TSource,TKey> (this System.Linq.IQueryable<TSource> source, System.Linq.Expressions.Expression<Func<TSource,TKey>> keySelector, System.Collections.Generic.IComparer<TSource>? comparer);
public static TSource? MaxBy<TSource,TKey> (this System.Linq.IQueryable<TSource> source, System.Linq.Expressions.Expression<Func<TSource,TKey>> keySelector, System.Collections.Generic.IComparer<TSource>? comparer);
Previously, when you wanted to order a collection by, let’s say, a year. You had to do something along the lines of:
var events = new[]
{
new Event(“Moonlanding”, 1969),
new Event(“Invention of Television”, 1925),
new Event(“Birth of the Internet”, 1965)
};
var earliest = events.OrderBy(e => e.Year).FirstOrDefault();
var latest = events.OrderBy(e => e.Year).FirstOrDefault();
In .NET 6 you can do:
var earliest = events.MinBy(e => e.Year);
var latest = events.MaxBy(e => e.Year);
Other new “By” functions:
- DistinctBy
- ExceptBy
- IntersectBy
- ThenBy
- UnionBy
.NET Multi-platform App UI (MAUI)
Microsoft has been talking about unification for a long time. In their perfect world, there would only be one .NET for all target platforms. They have started this process with .NET 5 and moved futher with this idea in .NET 6.
There now is a modern UI toolkit which is built on top of Xamarin. This enables you to share code across mobile and desktop apps, which means you can use .NET MAUI to build applications that can run on Windows, macOS, iOS and Android from a single code base.
The architecture of a .NET MAUI app is as follows:
Your code primarily interacts with the .NET MAUI API (1), where it will directly consume the native platform API (3). Alltough, your code may directly exercise platform APIs (2), if required.
Want more?
This is just a small sample of all the new features in .NET 6, there are many more, check them out for yourself here!
Or, if video has your preference, you can watch this video by Microsoft.