In this post, we're going to look at the new features that the latest version of C#, C#10, brings us. Remember, you need to install .NET 6 to use this version.
It will be officially released in a couple of weeks, but we already have the release candidate available.
Table of Contents
1 - Global usings in c#
In C#9 we saw how top-level programs were introduced to greatly reduce the amount of code needed to run a console app.
The goal of global usings is basically the same: reducing the amount of code we need. It's worth mentioning that this feature has (more or less) been available in Blazor since .NET 5 (c# 9).
It's about having our usings (that is, the namespaces we want to use) in a common place. 
We have two options for this:
1.1 - Global usings file
We can create a file in our project to include those global usings, just by declaring them, so we can use them from anywhere in that project.
global using System;
global using System.Collections.Generic;
global using System.Linq;
1.2 - Update the project's csproj file
Optionally, we can include them in the application's .csproj if we enable the <ImplicitUsings>enable</ImplicitUsings> annotation and include them in an ItemGroup.
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>
  <ItemGroup>
    <Using Include="System.Threading" />
  </ItemGroup>
  
</Project>Personally, I'm not sure which option I prefer, but what I do know is that I don't want a mix of both. Most likely, to prevent global usings from being scattered everywhere in the code, it would be a good idea to centralize it in the .csproj.
2 - File-scoped namespace declaration
By now, we all know what a namespace is, or a namespace in .NET. It's basically the logical space where our classes, methods, etc. are located. 
Until now (C#9 and earlier), we wrote it like this:
namespace csharp10
{
    class NamespacePorFichero
    {
    }
}But with C# 10, and with the same aim of reducing code, we can declare the namespace in a single line, without the curly braces, saving us both code and indentation.
namespace csharp10;
class NamespacePorFichero
{
}
3 - Minimal APIs
One of the biggest changes in this C# version is minimal APIs, about which I wrote a detailed post.To sum it up: Microsoft now lets us build APIs with very few lines of code, which is great.
using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Hosting;
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hola NetMentor!");
app.Run();
4 - DateOnly and TimeOnly
In C#, as in any language, there are always date and time issues: when does a date correspond to what, etc.
Some time ago, I made a little YouTube short explaining why you should use UTC when storing times. But sometimes, you only need either a date or just the time. 
For that, we now have DateOnly and TimeOnly types.
4.1 - DateOnly in C#
Using and understanding DateOnly is very simple, it just stores the date, which can be really useful, for example, when marking vacation days in HR software. We've all had to store time as 00:00:00 in work scenarios.
Now you just use DateOnly and it's very easy. In fact, you can use DateTime.Now as a reference:
DateOnly date = DateOnly.FromDateTime(DateTime.Now);
4.2 - TimeOnly in C#
Similarly, you can get the time from a DateTime object, pass a string and parse the time, or, probably more sensibly, create it from a TimeSpan.
TimeOnly time = TimeOnly.FromTimeSpan(TimeSpan.FromHours(13));
5 - Lambda improvements
This feature is tied to delegates and lambda expressions. We know we can declare a delegate in a single line, but if we use var instead of the delegate type, I personally prefer specifying the type, but I understand many developers like var.
Func<string> lambdaExpression = () => "hola Netmentor.";
var ejemplo = () => "hola Netmentor."; //In C#9, this doesn't compile.- Note: Also, C# 10 lets us specify the return type of the expression, which wasn't allowed before
Func<string?> conRetorno = string? () => null;
6 - PriorityQueue collection
A new collection type, a queue that lets us set the priority of the items we put in it.
- You can see more details in this post.
PriorityQueue<string, int> colaPrioridad = new PriorityQueue<string, int>();
colaPrioridad.Enqueue("Opel", 2);
colaPrioridad.Enqueue("Audi", 1);
colaPrioridad.Enqueue("BMW", 3); 
7 - LINQ improvements
7.1 - Max/MaxBy and Min/MinBy
After a long wait, LINQ finally gets the ability to get the maximum or minimum value of a list, based on an attribute in case of an object.
What we used to do with .OrderBy(x=>x.Prioridad).First(); can now be done with MaxBy()/MinBy().
The difference between using .Max()/.Min() and .MaxBy()/.MinBy() is in the result: when we use .Max(), we get the max value of the compared element, but with .MaxBy() we get the full object: 
List<MaxMinEjemplo> ejemploLista = new()
{
    new(3, "prioridad 3"),
    new(2, "prioridad 2"),
    new(10, "prioridad 10"),
    new(8, "prioridad 8")
};
int max = ejemploLista.Max(x=>x.Prioridad); //Returns 10 (as integer)
MaxMinEjemplo? maxBy = ejemploLista.MaxBy(x => x.Prioridad);//Returns the object with priority 10
7.2 - IEnumerable Chunk
Finally, here's a feature that lets us take a list (or any type implementing IEnumerable) and make chunks out of it, in other words, if you have a list of 1000 items, you can process them in blocks of 10, 100, 500, or whatever you specify.
I'm personally really glad to see this, as I've had to implement it manually in more than one company in the past.
It works pretty simply: you have a list and call the .chunk() method, passing the block size you want. 
IEnumerable<int> listInts = Enumerable.Range(0, 1000);//list of 1000 items
int chunkSize = 50;
foreach (var chunk in listInts.Chunk(chunkSize)) //returns a list with the indicated size. 
{
    //Parallel calls
    Parallel.ForEach(chunk, (item) =>
    {
        Console.WriteLine($"Simulamos get con id {item}");
    });
}
7.3 - Improved Take method
When we want to get part of a list and print it, we've always had to skip a certain number of items and then take the ones we want.
In C# 10, we can specify a range directly:
List<int> listInts = Enumerable.Range(0, 1000).ToList();//list of 1000 items
var resultCsharp9 = listInts.Skip(10).Take(15);
var resultCsharp10 = listInts.Take(10..25);
8 - Property patterns
In recent C# versions, Microsoft has given us what's called pattern matching and features for it. In this case, the improvement is that we can access child element properties using dot notation, whereas in C#9 it was much more verbose and complex, especially if the property was nested several levels deep within the main object.
public record Subtipo(int Edad);
public record Persona(string Nombre, Subtipo subtipo);
public void Ejemplo()
{
    Persona persona = new Persona("Ivan", new(29));
    string resultado = ExpressionCsharp10(persona);
}
private static string ExpressionCsharp10(Persona persona) =>
    persona switch
    {
        { subtipo.Edad: >= 18 } => "es mayor de edad",
        { subtipo.Edad: < 18 } => "es menor de edad",
        _ => throw new NotSupportedException()
    };
//This example is in c#9 and very verbose
private static string ExpressionCsharp9(Persona persona) =>
    persona switch
    {
        { subtipo: { Edad: >= 18 } } => "es mayor de edad",
        { subtipo: { Edad: < 18 } } => "es menor de edad",
        _ => throw new NotSupportedException()
    };
9 - Records improvements
Throughout my posts, I always recommend using records when possible, as I'm a big fan of immutability. I'm really glad to see the Microsoft team improving the language to make records better.
9.1 - Simplifying record definition in C# 10
First off, as you might have noticed while reading, now you no longer have to create records with properties; in the method definition you just write the constructor in one line, and the compiler gets that they're properties behind the scenes.
public record Persona(string Nombre, int Edad);
public void Ejemplo()
{
    Persona persona = new("Ivan", 28);
    Console.WriteLine($"El nombre es: {persona.Nombre}");
}As you can see, we can access the properties with no problem.
9.2 - record structs in C# 10
By default, when we define a record, it gets compiled as a class behind the scenes. But now, from C#10 onward, we can define record structs, simply by indicating it in the definition:
public record struct Notas(int Matematicas, int Ingles);
9.3 - Sealed ToString in records
By default, every object has a method called .ToString(), which is, for example, used in string interpolation. In the workplace, it's not uncommon to see this method overridden:
public record Persona(string Nombre, int Edad)
{
    public override string ToString()
    {
        return $"El nombre es: {Nombre} y su edad es: {Edad}";
    }
}
public void Ejemplo2()
{
    Persona persona = new("Ivan", 28);
    Console.WriteLine(persona);
}Now, we can mark this method as sealed (see sealed) so that if we have a type that implements our record, we can't override the .ToString() method:
public record Persona(string Nombre, int Edad)
{
    public sealed override string ToString()
    {
        return $"El nombre es: {Nombre} y su edad es: {Edad}";
    }
}
public record Alumno : Persona
{
    private Notas Notas;
    public Alumno(string nombre, int edad, Notas notas) : base(nombre, edad)
    {
        Notas = notas;
    }
    //Gives an error and won't compile
    public override string ToString()
    {
        return $"El nombre es: {Nombre} " +
            $"y su nota en matematicas es {Notas.Matematicas}";
    }
}
Since the Persona type has the .ToString() method sealed, the code won't compile if the Alumno type tries to override .ToString().
10 - String interpolation in constants
Not much else to say, the name says it all. From C# 10, you can use string interpolation in constants, something that wasn't possible before. Now you can define the following:
public const string Nombre = "Netmentor";
public const string Saludo = $"Hola {Nombre}!";
//This is how it was done in c# 9
public static readonly string SaludoCsharp9 =  $"Hola {Nombre}!";
Conclusion:
In this post we've seen what improvements were introduced in C# 10 compared to C# 9.
If there is any problem you can add a comment bellow or contact me in the website's contact form
 
                    