Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Ce tutoriel vous apprend plusieurs fonctionnalités dans .NET et le langage C#. Vous apprendrez ce qui suit :
- Principes de base de l’interface CLI .NET
- Structure d’une application console C#
- E/S console
- Principes de base des API d’E/S de fichier dans .NET
- Principes de base de la programmation asynchrone basée sur des tâches dans .NET
Vous allez créer une application qui lit un fichier texte et renvoie le contenu de ce fichier texte à la console. La sortie sur la console est rythmée pour correspondre à la lecture à haute voix. Vous pouvez accélérer ou ralentir le rythme en appuyant sur les touches «< » (inférieures à) ou «> » (supérieures à). Vous pouvez exécuter cette application sur Windows, Linux, macOS ou dans un conteneur Docker.
Il existe de nombreuses fonctionnalités dans ce tutoriel. Créons-les un par un.
Conditions préalables
- La dernière version du SDK .NET
- Éditeur de code Visual Studio
- Le DevKit C#
Créer l’application
La première étape consiste à créer une application. Ouvrez une invite de commandes et créez un répertoire pour votre application. Réglez-le comme répertoire actuel. Saisissez la commande dotnet new console à l’invite. Par exemple:
E:\development\VSprojects>mkdir teleprompter
E:\development\VSprojects>cd teleprompter
E:\development\VSprojects\teleprompter>dotnet new console
The template "Console Application" was created successfully.
Processing post-creation actions...
Running 'dotnet restore' on E:\development\VSprojects\teleprompter\teleprompter.csproj...
Determining projects to restore...
Restored E:\development\VSprojects\teleprompter\teleprompter.csproj (in 78 ms).
Restore succeeded.
Cela crée les fichiers de démarrage pour une application « Hello World » de base.
Avant de commencer à apporter des modifications, nous allons exécuter l’application Hello World simple. Après avoir créé l’application, tapez dotnet run à l’invite de commandes. Cette commande exécute le processus de restauration du package NuGet, crée l’exécutable de l’application et lance l’exécutable.
Le code d’application Hello World simple est tout en Program.cs. Ouvrez ce fichier avec votre éditeur de texte favori. Remplacez le code dans Program.cs par le code suivant :
namespace TeleprompterConsole;
internal class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
En haut du fichier, voyez une déclaration namespace. Comme d’autres langages orientés objet que vous avez utilisés, C# utilise des espaces de noms pour organiser les types. Ce programme Hello World n’est pas différent. Vous pouvez voir que le programme se trouve dans l’espace de noms avec le nom TeleprompterConsole.
Lecture et affichage du fichier
La première fonctionnalité à ajouter est la possibilité de lire un fichier texte et d’afficher tout ce texte dans la console. Tout d’abord, nous allons ajouter un fichier texte. Copiez le fichier sampleQuotes.txt à partir du dépôt GitHub pour cet exemple dans votre répertoire de projet. Cela servira de script pour votre application. Pour plus d’informations sur le téléchargement de l’exemple d’application pour ce didacticiel, consultez les instructions fournies dans Exemples et tutoriels.
Ensuite, ajoutez la méthode suivante dans votre Program classe (juste en dessous de la Main méthode) :
static IEnumerable<string> ReadFrom(string file)
{
string? line;
using (var reader = File.OpenText(file))
{
while ((line = reader.ReadLine()) != null)
{
yield return line;
}
}
}
Cette méthode est un type spécial de méthode C# appelée méthode itérateur. Les méthodes d’itérateur retournent des séquences évaluées paresseusement. Cela signifie que chaque élément de la séquence est généré, car il est demandé par le code qui consomme la séquence. Les méthodes d’itérateur sont des méthodes qui contiennent une ou plusieurs yield return instructions. L’objet retourné par la ReadFrom méthode contient le code pour générer chaque élément de la séquence. Dans cet exemple, cela implique la lecture de la ligne de texte suivante à partir du fichier source et le renvoi de cette chaîne. Chaque fois que le code appelant demande l’élément suivant de la séquence, le code lit la ligne de texte suivante du fichier et le retourne. Lorsque le fichier est entièrement lu, la séquence indique qu’il n’y a plus d’éléments.
Il existe deux éléments de syntaxe C# qui peuvent vous être nouveaux. L’instruction using de cette méthode gère le nettoyage des ressources. La variable initialisée dans l’instruction using (readerdans cet exemple) doit implémenter l’interface IDisposable . Cette interface définit une méthode unique, Disposequi doit être appelée lorsque la ressource doit être libérée. Le compilateur génère l’appel lorsque l’exécution atteint l’accolade fermante de l’instruction using. Le code généré par le compilateur garantit que la ressource est libérée même si une exception est provoquée par le code dans le bloc défini par l'instruction 'using'.
La reader variable est définie à l’aide du var mot clé. var définit une variable locale implicitement typée. Cela signifie que le type de la variable est déterminé par le type de compilation de l’objet affecté à la variable. Voici la valeur de retour de la OpenText(String) méthode, qui est un StreamReader objet.
Maintenant, nous allons renseigner le code pour lire le fichier dans la Main méthode :
var lines = ReadFrom("sampleQuotes.txt");
foreach (var line in lines)
{
Console.WriteLine(line);
}
Exécutez le programme (à l’aide dotnet run) et vous pouvez voir chaque ligne imprimée dans la console.
Ajout de temporisations et mise en forme de la sortie
Ce que vous avez est affiché beaucoup trop rapidement pour être lu à haute voix. Vous devez maintenant ajouter les retards dans la sortie. Au démarrage, vous allez créer un certain code principal qui active le traitement asynchrone. Toutefois, ces premières étapes suivent quelques anti-modèles. Les anti-modèles sont signalés dans les commentaires lorsque vous ajoutez le code, et le code sera mis à jour dans les étapes ultérieures.
Cette section comporte deux étapes. Tout d’abord, vous allez mettre à jour la méthode d’itérateur pour retourner des mots uniques au lieu de lignes entières. C’est fait avec ces modifications. Remplacez l’instruction yield return line; par le code suivant :
var words = line.Split(' ');
foreach (var word in words)
{
yield return word + " ";
}
yield return Environment.NewLine;
Ensuite, vous devez modifier la façon dont vous utilisez les lignes du fichier et ajouter un délai après l’écriture de chaque mot. Remplacez l’instruction Console.WriteLine(line) dans la Main méthode par le bloc suivant :
Console.Write(line);
if (!string.IsNullOrWhiteSpace(line))
{
var pause = Task.Delay(200);
// Synchronously waiting on a task is an
// anti-pattern. This will get fixed in later
// steps.
pause.Wait();
}
Exécutez l’exemple et vérifiez la sortie. À présent, chaque mot unique est imprimé, suivi d’un délai de 200 ms. Toutefois, la sortie affichée présente certains problèmes, car le fichier texte source comporte plusieurs lignes qui ont plus de 80 caractères sans saut de ligne. Ce qui peut être difficile à lire lors du défilement. C’est facile à corriger. Vous allez simplement suivre la longueur de chaque ligne et générer une nouvelle ligne chaque fois que la longueur de ligne atteint un certain seuil. Déclarez une variable locale après la déclaration de words dans la méthode ReadFrom qui contient la longueur de ligne :
var lineLength = 0;
Ensuite, ajoutez le code suivant après l’instruction yield return word + " "; (avant l’accolade fermante) :
lineLength += word.Length + 1;
if (lineLength > 70)
{
yield return Environment.NewLine;
lineLength = 0;
}
Exécutez l’exemple et vous serez en mesure de lire à haute voix à son rythme préconfiguré.
Tâches asynchrones
Dans cette dernière étape, vous allez ajouter le code pour écrire la sortie de manière asynchrone dans une tâche, tout en exécutant une autre tâche pour lire l’entrée de l’utilisateur s’il souhaite accélérer ou ralentir l’affichage du texte, ou arrêter complètement l’affichage du texte. Cette opération comporte quelques étapes et, à la fin, vous disposez de toutes les mises à jour dont vous avez besoin. La première étape consiste à créer une méthode de retour asynchrone Task qui représente le code que vous avez créé jusqu’à présent pour lire et afficher le fichier.
Ajoutez cette méthode à votre Program classe (elle est extraite du corps de votre Main méthode) :
private static async Task ShowTeleprompter()
{
var words = ReadFrom("sampleQuotes.txt");
foreach (var word in words)
{
Console.Write(word);
if (!string.IsNullOrWhiteSpace(word))
{
await Task.Delay(200);
}
}
}
Vous remarquerez deux modifications. Tout d’abord, dans le corps de la méthode, au lieu d’appeler Wait() de façon synchrone attendre la fin d’une tâche, cette version utilise le await mot clé. Pour ce faire, vous devez ajouter le async modificateur à la signature de méthode. Cette méthode retourne un Task. Notez qu’il n’existe aucune instruction return qui retourne un Task objet. Au lieu de cela, cet Task objet est créé par le code généré par le compilateur lorsque vous utilisez l’opérateur await . Vous pouvez imaginer que cette méthode retourne lorsqu’elle atteint un await. Le retour Task indique que le travail n’a pas terminé. La méthode reprend une fois la tâche attendue terminée. Une fois l’exécution terminée, le retour Task indique qu’il est terminé.
Le code appelant peut surveiller la valeur retournée Task pour déterminer à quel moment elle est terminée.
Ajoutez un await mot clé avant l’appel à ShowTeleprompter:
await ShowTeleprompter();
Cela vous oblige à modifier la signature de méthode Main en :
static async Task Main(string[] args)
En savoir plus sur la async Main méthode dans notre section notions de base.
Ensuite, vous devez écrire la deuxième méthode asynchrone pour lire à partir de la console et regarder les touches «< » (inférieures à), «> » (supérieures à) et « X » ou « x ». Voici la méthode que vous ajoutez pour cette tâche :
private static async Task GetInput()
{
var delay = 200;
Action work = () =>
{
do {
var key = Console.ReadKey(true);
if (key.KeyChar == '>')
{
delay -= 10;
}
else if (key.KeyChar == '<')
{
delay += 10;
}
else if (key.KeyChar == 'X' || key.KeyChar == 'x')
{
break;
}
} while (true);
};
await Task.Run(work);
}
Cela crée une expression lambda pour représenter un Action délégué qui lit une clé à partir de la console et modifie une variable locale représentant le délai lorsque l’utilisateur appuie sur les touches «< » (inférieures à) ou «> » (supérieures à). La méthode de délégué se termine lorsque l’utilisateur appuie sur les touches « X » ou « x », ce qui permet à l’utilisateur d’arrêter l’affichage du texte à tout moment. Cette méthode utilise ReadKey() pour bloquer et attendre que l’utilisateur appuie sur une touche.
Il est temps de créer une classe qui peut gérer les données partagées entre ces deux tâches. Cette classe contient deux propriétés publiques : le délai et un indicateur Done pour indiquer que le fichier a été entièrement lu :
using static System.Math;
namespace TeleprompterConsole;
internal class TelePrompterConfig
{
public int DelayInMilliseconds { get; private set; } = 200;
public void UpdateDelay(int increment) // negative to speed up
{
var newDelay = Min(DelayInMilliseconds + increment, 1000);
newDelay = Max(newDelay, 20);
DelayInMilliseconds = newDelay;
}
public bool Done { get; private set; }
public void SetDone()
{
Done = true;
}
}
Créer un fichier ; il peut s’agir de n’importe quel nom se terminant par .cs. Par exemple, TelePrompterConfig.cs. Collez le code de classe TelePrompterConfig, enregistrez et fermez. Mettez cette classe dans le namespace TeleprompterConsole comme indiqué. Notez que l'instruction using static vous permet de référencer les méthodes Min et Max sans les noms de classe ou d'espace de noms englobants. Une using static instruction importe les méthodes d’une classe. Cela diffère de l’instruction using sans static, qui importe toutes les classes à partir d’un espace de noms.
Ensuite, vous devez mettre à jour les méthodes ShowTeleprompter et GetInput pour utiliser le nouvel objet config. Pour terminer cette fonctionnalité, vous devez créer une nouvelle async Task méthode de retour qui démarre ces deux tâches (GetInput et ShowTeleprompter) et gère également les données partagées entre ces deux tâches. Créez une tâche RunTelePrompter pour démarrer les deux tâches et quitter une fois la première tâche terminée :
private static async Task RunTeleprompter()
{
var config = new TelePrompterConfig();
var displayTask = ShowTeleprompter(config);
var speedTask = GetInput(config);
await Task.WhenAny(displayTask, speedTask);
}
La nouvelle méthode ici est l’appel WhenAny(Task[]) . Cela crée une Task opération qui se termine dès que l’une des tâches de sa liste d’arguments est terminée.
Ensuite, vous devez mettre à jour les méthodes ShowTeleprompter et GetInput pour utiliser l'objet config pour le délai. L’objet config est passé en tant que paramètre à ces méthodes. Utilisez copier/coller pour remplacer complètement les méthodes par le nouveau code ici. Vous pouvez voir que le code utilise des attributs et appelle des méthodes à partir de l’objet config :
private static async Task ShowTeleprompter(TelePrompterConfig config)
{
var words = ReadFrom("sampleQuotes.txt");
foreach (var word in words)
{
Console.Write(word);
if (!string.IsNullOrWhiteSpace(word))
{
await Task.Delay(config.DelayInMilliseconds);
}
}
config.SetDone();
}
private static async Task GetInput(TelePrompterConfig config)
{
Action work = () =>
{
do {
var key = Console.ReadKey(true);
if (key.KeyChar == '>')
config.UpdateDelay(-10);
else if (key.KeyChar == '<')
config.UpdateDelay(10);
else if (key.KeyChar == 'X' || key.KeyChar == 'x')
config.SetDone();
} while (!config.Done);
};
await Task.Run(work);
}
À présent, vous devez effectuer une mise à jour Main pour appeler RunTeleprompter au lieu de ShowTeleprompter:
await RunTeleprompter();
Conclusion
Ce tutoriel vous a montré un certain nombre de fonctionnalités autour du langage C# et des bibliothèques .NET Core liées à l’utilisation d’applications console. Vous pouvez vous appuyer sur cette connaissance pour en savoir plus sur la langue et les classes introduites ici. Vous avez vu les bases des E/S de fichier et de console, le blocage et l’utilisation non bloquante de la programmation asynchrone basée sur les tâches, une visite guidée du langage C# et la façon dont les programmes C# sont organisés et l’interface CLI .NET.
Pour plus d’informations sur les E/S de fichier, consultez Fichier et flux d’E/S. Pour plus d’informations sur le modèle de programmation asynchrone utilisé dans ce tutoriel, consultez Programmation asynchrone basée sur les tâches et programmation asynchrone.