Archives pour la catégorie Non classé

.Net Core 3.0 s’annonce dément… Et C# 8 tout autant !

.NET Core 3.0 n’est  qu’en preview, mais s’il s’annonce comme le plus attendu des « trois .NET », à savoir .NET Framework, .NET Standard et .NET Core. Ceci, pour une raison particulière, il annonce l’arrivé d’un composant dénommé Windows Desktop qui permettra l’utilisation des WinForms ou de WPF en .NET Core. Cette fonctionnalité tant attendue sera désormais disponible, mais il ne faut toutefois pas s’emballer. Oui, vous pourrez coder des applications lourdes, non ces applications ne pourront pas se déployer sur du Linux à l’image des applications JavaFX ou Swing du monde Java.

Quel intérêt ?

Rappelons-nous l’origine de .Net Core. Certes, l’objectif était de fonctionner en dehors de Windows, c’est d’ailleurs la raison pour laquelle Microsoft s’était attaqué à ASP.NET, mais n’oublions pas ses apports : une indépendance vis-à-vis de la version du framework installée sur le PC ainsi qu’une indépendance vis-à-vis de Windows update ; ce qui garantie de la stabilité en cas d’update malencontreux du framework.

.NET Core a en outre l’intérêt d’être entièrement opensource ; toutes les sources sont disponibles sur GitHub et enrichies de nombreuses contributions.

Il y a également tout un volet sur les performances ; donc oui, WinForms ou WPF en .NET Core a un grand intérêt, mais attention tout de même, certains concepts disparaissent, et pas des moindres (je vous laisse chercher lesquels).

.NET Core ne s’arrête pas là ; il nous propose C#8 et ce, dès la Preview 2.0.

Que nous apporte C# 8 ?

Using simplifiés grâce aux déclarations using

Vous n’êtes plus obligé d’utiliser les instructions using qui impliquent des accolades. Les déclarations using garantissent la libération des ressources dès que l’objet sort de la portée ; ce qui rend plus lisible le code.

{
    using var options = Parse(args);
    if (options["verbose"]) { WriteLine("Logging..."); }
} // le dispose s'applique ici

Index et Range

Quelques fonctionnalités sympathiques notamment les index et les plages . Les Index se créent à partir d’un entier qui compte à partir du début (il faut préfixer par l’opérateur « ^ » pour partir de la fin ». Les Index permettent de se repérer plus rapidement dans un tableau.

Les plages (type Range) permettent l’extraction d’une tranche dans un tableau.

Index i1 = 3; // On part de 3 à partir du début
Index i2 = ^4; // On part de 4 à partir de la fin
int[] a = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
Console.WriteLine($"{a[i1]}, {a[i2]}"); // "3, 6"
var slice = a[i1..i2]; // { 3, 4, 5 }

Flux asynchrones
Microsoft introduit IAsyncEnumerable, une version asynchrone de IEnumerable.  Il est possible d’en récupérer les élements dans un

await foreach

 ou de produire dans éléments grâce à un 

yield return

.

Mais pourquoi un IAsyncEnumerable ?
Tout dépend de votre secteur d’activité mais ceux qui travaillent dans l’IOT par exemple ont besoin d’itérer sur un IEnumerable de façon asynchrone tout en utilisant un yield pour effectuer une action quand les données arrivent. Un exemple classique est un composant IOT qui veut recevoir et traiter des données quand elles arrivent sans bloquer la CPU pendant qu’il attend.

Mais nous avons déjà un async ?
Parfaitement, sauf que le comportment n’est pas celui attendu. Imaginons le code suivant :

static async Task Main(string[] args)
{
    foreach(var data in await GetData())
    {
        Console.WriteLine(data);
    }
 
    Console.ReadLine();
}
 
static async Task<IEnumerable<int>> GetData()
{
    List<int> infos = new List<int>();
    for (int i = 1; i <= 10; i++)
    {
        await Task.Delay(1000); //On simule une attente
        infos.Add(i);
    }
 
    return infos;
}

Ce code qui itère bien en asynchrone et qui ne bloque pas le thread ne nous renverra pas les données aussitôt qu’il les reçoit. Il faut en effet attendre 10 secondes (10 itérations de 1 seconde) pour remplir une liste que l’on ne récupère
qu’à la fin.

Voyons maintenant le code avec IAsyncEnumerable.

static async Task Main(string[] args)
{
    await foreach(var data in GetData())
    {
        Console.WriteLine(data);
    }
 
    Console.ReadLine();
}
 
static async IAsyncEnumerable<int> GetData()
{
    for (int i = 1; i <= 10; i++)
    {
        await Task.Delay(1000); //On simule une attente
        yield return i;
    }
}

En premier lieu, notez le await foreach. On n’attend plus sur la récupération de données mais bien sur la récupération d’un item. Ensuite, on ne retourne plus un IEnumerable mais un IAsyncEnumerable. enfin le yield nous permet l’envoi des données au fil de l’eau.

Grâce à ce code nous n’avons plus à attendre 10 secondes, nous traitons les données au fur et à mesure qu’elles arrivent.

Expressions switch

C# 7 a introduit les switch patterns qui permettent d’écrire le code suivant

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

class Example
{
    static void Main(string[] args)
    {
        int[] values = { 2, 4, 6, 8, 10 };
        ShowCollectionInformation(values);

        var names = new List();
        names.AddRange( new string[] { "Adam", "Abigail", "Bertrand", "Bridgette" } );
        ShowCollectionInformation(names);

        List numbers = null;
        ShowCollectionInformation(numbers);
    }

    private static void ShowCollectionInformation(object coll)
    {
        switch (coll)
        {
            case Array arr:
                Console.WriteLine($"Un tableau de {arr.Length} elements.");
                break;
            case IEnumerable ieInt:
                Console.WriteLine($"Moyenne: {ieInt.Average(s => s)}");
                break;
            case IList list:
                Console.WriteLine($"{list.Count} items");
                break;
            case IEnumerable ie:
                string result = "";
                foreach (var e in ie)
                result += "${e} ";
                Console.WriteLine(result);
                break;
            case null:
                break;
            default:
                Console.WriteLine($"Une instance de type {coll.GetType().Name}");
                break;
        }
    }
}
// L'exemple affiche le texte suivant :
// Un tableau de 5 elements.
//

C’est pratique, assurément.
Mais C# 8 introduit les Switch expressions qui sont une façon, en général, plus propre de faire un switch et de retourner une valeur. Les Switch expressions sont complètement intégrer au « pattern matching » et utilise le « discard pattern » _, pour renvoyer une valeur par défaut.

Voici un exemple

static string Display(object o) => o switch
{
    Point { X: 0, Y: 0 }         => "origin",
    Point { X: var x, Y: var y } => $"({x}, {y})",
    _                            => "unknown"
};

Dans l’exemple si dessus, il y a deux patterns. Le premier est un type pattern qui s’intéresse à déterminer si o est de type Point. Il y a ensuite le property pattern entre parenthèses. Enfin il y a _ qui est le pattern par défaut s’il n’y a pas de correspondance.

Pour plus d’informations, vous pouvez vous référer au lien suivant : https://devblogs.microsoft.com/dotnet/do-more-with-patterns-in-c-8-0/