Partager via


Tutoriel : Créer des programmes C# basés sur des fichiers

Les applications basées sur des fichiers sont des programmes contenus dans un seul *.cs fichier généré et exécuté sans fichier de projet (*.csproj) correspondant. Les applications basées sur des fichiers sont idéales pour l’apprentissage de C# car elles ont moins de complexité : l’ensemble du programme est stocké dans un seul fichier. Les applications basées sur des fichiers sont également utiles pour créer des utilitaires de ligne de commande. Sur les plateformes Unix, les applications basées sur des fichiers peuvent être exécutées à l’aide #! de directives (shebang). Dans ce tutoriel, vous allez :

  • Créez un programme basé sur des fichiers.
  • Ajoutez la prise en charge de shebang#! () Unix.
  • Lire les arguments de ligne de commande.
  • Gérer l’entrée standard.
  • Écrire une sortie d’art ASCII.
  • Traiter les arguments de ligne de commande.
  • Utilisez les résultats de ligne de commande analysés.
  • Testez l’application finale.

Vous créez un programme basé sur des fichiers qui écrit du texte en tant qu’art ASCII. L’application est contenue dans un seul fichier, utilise des packages NuGet qui implémentent certaines des fonctionnalités principales.

Prerequisites

Créer un programme basé sur des fichiers

  1. Ouvrez Visual Studio Code et créez un fichier nommé AsciiArt.cs. Tapez le texte suivant :

    Console.WriteLine("Hello, world!");
    
  2. Enregistrez le fichier. Ensuite, ouvrez le terminal intégré dans Visual Studio Code et tapez :

    dotnet run AsciiArt.cs
    

La première fois que vous exécutez ce programme, l’hôte dotnet génère l’exécutable à partir de votre fichier source, stocke les artefacts de build dans un dossier temporaire, puis exécute l’exécutable créé. Vous pouvez vérifier cette expérience en tapant dotnet run AsciiArt.cs à nouveau. Cette fois, l’hôte dotnet détermine que l’exécutable est actif et exécute l’exécutable sans le générer à nouveau. Vous ne voyez aucune sortie de build.

Les étapes précédentes montrent que les applications basées sur des fichiers ne sont pas des fichiers de script. Il s’agit de fichiers sources C# générés à l’aide d’un fichier projet généré dans un dossier temporaire. Une des lignes de sortie affichées lorsque vous avez créé le programme doit ressembler à ceci (sur Windows) :

AsciiArt succeeded (7.3s) → AppData\Local\Temp\dotnet\runfile\AsciiArt-85c58ae0cd68371711f06f297fa0d7891d0de82afde04d8c64d5f910ddc04ddc\bin\debug\AsciiArt.dll

Sur les plateformes unix, le dossier de sortie est similaire à ce qui suit :

AsciiArt succeeded (7.3s) → Library/Application Support/dotnet/runfile/AsciiArt-85c58ae0cd68371711f06f297fa0d7891d0de82afde04d8c64d5f910ddc04ddc/bin/debug/AsciiArt.dll

Cette sortie vous indique où sont placés les fichiers temporaires et les sorties de build. Tout au long de ce tutoriel, chaque fois que vous modifiez le fichier source, l’hôte dotnet met à jour l’exécutable avant son exécution.

Les applications basées sur des fichiers sont des programmes C# standard. La seule limitation est qu’elles doivent être écrites dans un fichier source. Vous pouvez utiliser des instructions de niveau supérieur ou une méthode classique Main comme point d’entrée. Vous pouvez déclarer tous les types : classes, interfaces et structs. Vous pouvez structurer les algorithmes dans un programme basé sur des fichiers identiques à ceux que vous feriez dans n’importe quel programme C#. Vous pouvez même déclarer plusieurs espaces de noms pour organiser votre code. Si vous trouvez qu’un programme basé sur un fichier augmente trop grand pour un seul fichier, vous pouvez le convertir en programme basé sur un projet et fractionner la source en plusieurs fichiers. Les applications basées sur des fichiers sont un excellent outil de prototypage. Vous pouvez commencer à expérimenter avec une surcharge minimale pour prouver les concepts et générer des algorithmes.

Prise en charge du shebang Unix (#!)

Note

La prise en charge des #! directives s’applique uniquement sur les plateformes Unix. Il n’existe pas de directive similaire pour Que Windows exécute directement un programme C#. Sur Windows, vous devez utiliser dotnet run sur la ligne de commande.

Sur unix, vous pouvez exécuter des applications basées sur des fichiers directement, en tapant le nom du fichier source sur la ligne de commande au lieu de dotnet run. Vous devez apporter deux modifications :

  1. Définissez les autorisations d’exécution sur le fichier source :

    chmod +x AsciiArt.cs
    
  2. Ajoutez une directive shebang (#!) comme première ligne du AsciiArt.cs fichier :

    #!/usr/local/share/dotnet/dotnet run
    

L’emplacement de dotnet peut être différent sur différentes installations unix. Utilisez la commande which dotnet pour localiser l’hôte dotnet dans votre environnement.

Vous pouvez également utiliser #!/usr/bin/env dotnet pour résoudre automatiquement le chemin dotnet à partir de la variable d’environnement PATH :

#!/usr/bin/env dotnet

Après avoir apporté ces deux modifications, vous pouvez exécuter le programme directement à partir de la ligne de commande :

./AsciiArt.cs

Si vous préférez, vous pouvez supprimer l’extension afin de pouvoir taper ./AsciiArt à la place. Vous pouvez ajouter le #! fichier source même si vous utilisez Windows. La ligne de commande Windows ne prend pas en charge #!, mais le compilateur C# autorise cette directive dans les applications basées sur des fichiers sur toutes les plateformes.

Lire les arguments de ligne de commande

À présent, écrivez tous les arguments sur la ligne de commande dans la sortie.

  1. Remplacez le contenu actuel par AsciiArt.cs le code suivant :

    if (args.Length > 0)
    {
        string message = string.Join(' ', args);
        Console.WriteLine(message);
    }
    
  2. Vous pouvez exécuter cette version en tapant la commande suivante :

    dotnet run AsciiArt.cs -- This is the command line.
    

    L’option -- indique que tous les arguments de commande suivants doivent être passés au programme AsciiArt. Les arguments This is the command line. sont passés sous la forme d’un tableau de chaînes, où chaque chaîne est un mot : This, , is, the, commandet line..

Cette version illustre ces nouveaux concepts :

  • Les arguments de ligne de commande sont passés au programme à l’aide de la variable argsprédéfinie. La args variable est un tableau de chaînes : string[]. Si la longueur args est égale à 0, cela signifie qu’aucun argument n’a été fourni. Sinon, chaque mot de la liste d’arguments est stocké dans l’entrée correspondante dans le tableau.
  • La string.Join méthode joint plusieurs chaînes en une seule chaîne, avec le séparateur spécifié. Dans ce cas, le séparateur est un espace unique.
  • Console.WriteLine écrit la chaîne dans la console de sortie standard, suivie d’une nouvelle ligne.

Gérer l’entrée standard

Cela gère correctement les arguments de ligne de commande. À présent, ajoutez le code pour gérer la lecture de l’entrée à partir d’une entrée standard (stdin) au lieu d’arguments de ligne de commande.

  1. Ajoutez la clause suivante else à l’instruction if que vous avez ajoutée dans le code précédent :

    else
    {
        while (Console.ReadLine() is string line && line.Length > 0)
        {
            Console.WriteLine(line);
        }
    }
    

    Le code précédent lit l’entrée de la console jusqu’à ce qu’une ligne vide soit une null lecture. (La Console.ReadLine méthode retourne null si le flux d’entrée est fermé en tapant ctrl+C.)

  2. Testez la lecture de l’entrée standard en créant un fichier texte dans le même dossier. Nommez le fichier input.txt et ajoutez les lignes suivantes :

    Hello from ...
    dotnet!
    
    You can create
    file-based apps
    in .NET 10 and
    C# 14
    
    Have fun writing
    useful utilities
    

    Gardez les lignes courtes afin qu’elles soient correctement mises en forme lorsque vous ajoutez la fonctionnalité pour utiliser l’art ASCII.

  3. Réexécutez le programme.

    Avec bash :

    cat input.txt | dotnet run AsciiArt.cs
    

    Ou, avec PowerShell :

    Get-Content input.txt | dotnet run AsciiArt.cs
    

À présent, votre programme peut accepter des arguments de ligne de commande ou une entrée standard.

Écrire une sortie ASCII Art

Ensuite, ajoutez un package qui prend en charge l’art ASCII, Color.Console. Pour ajouter un package à un programme basé sur des fichiers, vous utilisez la #:package directive.

  1. Ajoutez la directive suivante après la #! directive dans votre fichier AsciiArt.cs :

    #:package Colorful.Console@1.2.15
    

    Important

    La version 1.2.15 était la dernière version du package lors de la Colorful.Console dernière mise à jour de ce didacticiel. Consultez la page NuGet du package pour connaître la dernière version pour vous assurer d’utiliser une version de package avec les derniers correctifs de sécurité.

  2. Modifiez les lignes qui appellent Console.WriteLine à utiliser la méthode à la Colorful.Console.WriteAscii place :

    async Task WriteAsciiArt(AsciiMessageOptions options)
    {
        foreach (string message in options.Messages)
        {
            Colorful.Console.WriteAscii(message);
            await Task.Delay(options.Delay);
        }
    }
    
  3. Exécutez le programme et vous voyez la sortie de l’art ASCII au lieu du texte en écho.

Options de commande de processus

Ensuite, ajoutons l’analyse de ligne de commande. La version actuelle écrit chaque mot sous la forme d’une ligne de sortie différente. Les arguments de ligne de commande que vous avez ajoutés prennent en charge deux fonctionnalités :

  1. Citez plusieurs mots qui doivent être écrits sur une ligne :

    AsciiArt.cs "This is line one" "This is another line" "This is the last line"
    
  2. Ajoutez une --delay option pour suspendre entre chaque ligne :

    AsciiArt.cs --delay 1000
    

Les utilisateurs doivent pouvoir utiliser les deux arguments ensemble.

La plupart des applications de ligne de commande doivent analyser efficacement les arguments de ligne de commande pour gérer efficacement les options, les commandes et l’entrée utilisateur. La System.CommandLine bibliothèque fournit des fonctionnalités complètes pour gérer les commandes, les sous-commandes, les options et les arguments, ce qui vous permet de vous concentrer sur ce que fait votre application plutôt que sur la mécanique de l’analyse des entrées de ligne de commande.

La System.CommandLine bibliothèque offre plusieurs avantages clés :

  • Génération et validation automatiques du texte d’aide.
  • Prise en charge des conventions de ligne de commande POSIX et Windows.
  • Fonctionnalités d’achèvement des onglets intégrées.
  • Comportement d’analyse cohérent entre les applications.
  1. Ajoutez le System.CommandLine package. Ajoutez cette directive après la directive de package existante :

    #:package System.CommandLine@2.0.0
    

    Important

    La version 2.0.0 était la dernière version lorsque ce didacticiel a été mis à jour pour la dernière fois. S’il existe une version plus récente disponible, utilisez la dernière version pour vous assurer que vous disposez des derniers packages de sécurité. Consultez la page NuGet du package pour connaître la dernière version pour vous assurer d’utiliser une version de package avec les derniers correctifs de sécurité.

  2. Ajoutez les instructions using nécessaires en haut de votre fichier (après les directives et #! les #:package directives) :

    using System.CommandLine;
    using System.CommandLine.Parsing;
    
  3. Définissez l’option de délai et l’argument messages. Ajoutez le code suivant pour créer les objets et CommandLine.Option l’option CommandLine.Argument de ligne de commande et l’argument :

    Option<int> delayOption = new("--delay")
    {
        Description = "Delay between lines, specified as milliseconds.",
        DefaultValueFactory = parseResult => 100
    };
    
    Argument<string[]> messagesArgument = new("Messages")
    {
        Description = "Text to render."
    };
    

    Dans les applications en ligne de commande, les options commencent généralement par -- (tiret double) et peuvent accepter des arguments. L’option --delay accepte un argument entier qui spécifie le délai en millisecondes. Définit messagesArgument la façon dont les jetons restants après l’analyse des options sont analysés en tant que texte. Chaque jeton devient une chaîne distincte dans le tableau, mais le texte peut être entre guillemets pour inclure plusieurs mots dans un seul jeton. Par exemple, "This is one message" devient un seul jeton, tandis qu’il This is four tokens devient quatre jetons distincts.

    Le code précédent définit le type d’argument de l’option --delay et que les arguments sont un tableau de string valeurs. Cette application n’a qu’une seule commande. Vous utilisez donc la commande racine.

  4. Créez une commande racine et configurez-la avec l’option et l’argument. Ajoutez l’argument et l’option à la commande racine :

    RootCommand rootCommand = new("Ascii Art file-based program sample");
    
    rootCommand.Options.Add(delayOption);
    rootCommand.Arguments.Add(messagesArgument);
    
  5. Ajoutez le code pour analyser les arguments de ligne de commande et gérer les erreurs. Ce code valide les arguments de ligne de commande et stocke les arguments analysés dans l’objet System.CommandLine.ParseResult :

    ParseResult result = rootCommand.Parse(args);
    foreach (ParseError parseError in result.Errors)
    {
        Console.Error.WriteLine(parseError.Message);
    }
    if (result.Errors.Count > 0)
    {
        return 1;
    }
    

Le code précédent valide tous les arguments de ligne de commande. Si la validation échoue, les erreurs sont écrites dans la console et l’application se ferme.

Utiliser les résultats de la ligne de commande analysée

À présent, terminez l’application pour utiliser les options analysées et écrivez la sortie. Tout d’abord, définissez un enregistrement pour contenir les options analysées. Les applications basées sur des fichiers peuvent inclure des déclarations de type, telles que des enregistrements et des classes. Elles doivent être après toutes les instructions de niveau supérieur et toutes les fonctions locales.

  1. Ajoutez une record déclaration pour stocker les messages et la valeur de l’option de délai :

    public record AsciiMessageOptions(string[] Messages, int Delay);
    
  2. Ajoutez la fonction locale suivante avant la déclaration d’enregistrement. Cette méthode gère à la fois les arguments de ligne de commande et l’entrée standard, et retourne une nouvelle instance d’enregistrement :

    async Task<AsciiMessageOptions> ProcessParseResults(ParseResult result)
    {
        int delay = result.GetValue(delayOption);
        List<string> messages = [.. result.GetValue(messagesArgument) ?? Array.Empty<string>()];
    
        if (messages.Count == 0)
        {
            while (Console.ReadLine() is string line && line.Length > 0)
            {
                Colorful.Console.WriteAscii(line);
                await Task.Delay(delay);
            }
        }
        return new([.. messages], delay);
    }
    
  3. Créez une fonction locale pour écrire l’art ASCII avec le délai spécifié. Cette fonction écrit chaque message dans l’enregistrement avec le délai spécifié entre chaque message :

    async Task WriteAsciiArt(AsciiMessageOptions options)
    {
        foreach (string message in options.Messages)
        {
            Colorful.Console.WriteAscii(message);
            await Task.Delay(options.Delay);
        }
    }
    
  4. Remplacez la if clause que vous avez écrite précédemment par le code suivant qui traite les arguments de ligne de commande et écrivez la sortie :

    var parsedArgs = await ProcessParseResults(result);
    
    await WriteAsciiArt(parsedArgs);
    return 0;
    

Vous avez créé un type qui fournit une record structure aux options et arguments de ligne de commande analysés. Les nouvelles fonctions locales créent une instance de l’enregistrement et utilisent l’enregistrement pour écrire la sortie d’art ASCII.

Tester l’application finale

Testez l’application en exécutant plusieurs commandes différentes. Si vous rencontrez des problèmes, voici l’exemple terminé à comparer à ce que vous avez créé :

#!/usr/local/share/dotnet/dotnet run

#:package Colorful.Console@1.2.15
#:package System.CommandLine@2.0.0

using System.CommandLine;
using System.CommandLine.Parsing;

Option<int> delayOption = new("--delay")
{
    Description = "Delay between lines, specified as milliseconds.",
    DefaultValueFactory = parseResult => 100
};

Argument<string[]> messagesArgument = new("Messages")
{
    Description = "Text to render."
};

RootCommand rootCommand = new("Ascii Art file-based program sample");

rootCommand.Options.Add(delayOption);
rootCommand.Arguments.Add(messagesArgument);

ParseResult result = rootCommand.Parse(args);
foreach (ParseError parseError in result.Errors)
{
    Console.Error.WriteLine(parseError.Message);
}
if (result.Errors.Count > 0)
{
    return 1;
}

var parsedArgs = await ProcessParseResults(result);

await WriteAsciiArt(parsedArgs);
return 0;

async Task<AsciiMessageOptions> ProcessParseResults(ParseResult result)
{
    int delay = result.GetValue(delayOption);
    List<string> messages = [.. result.GetValue(messagesArgument) ?? Array.Empty<string>()];

    if (messages.Count == 0)
    {
        while (Console.ReadLine() is string line && line.Length > 0)
        {
            // <WriteAscii>
            Colorful.Console.WriteAscii(line);
            // </WriteAscii>
            await Task.Delay(delay);
        }
    }
    return new([.. messages], delay);
}

async Task WriteAsciiArt(AsciiMessageOptions options)
{
    foreach (string message in options.Messages)
    {
        Colorful.Console.WriteAscii(message);
        await Task.Delay(options.Delay);
    }
}

public record AsciiMessageOptions(string[] Messages, int Delay);

Dans ce tutoriel, vous avez appris à créer un programme basé sur des fichiers, où vous générez le programme dans un seul fichier C#. Ces programmes n’utilisent pas de fichier projet et peuvent utiliser la directive sur les #! systèmes unix. Les apprenants peuvent créer ces programmes après avoir essayé nos didacticiels en ligne et avant de créer des applications basées sur des projets plus volumineuses. Les applications basées sur des fichiers sont également une plateforme idéale pour les utilitaires de ligne de commande.