Freigeben über


Lernprogramm: Erstellen dateibasierter C#-Programme

Dateibasierte Apps sind Programme , die in einer einzelnen *.cs Datei enthalten sind, die ohne eine entsprechende Projektdatei (*.csproj) erstellt und ausgeführt werden. Dateibasierte Apps eignen sich ideal zum Erlernen von C#, da sie weniger Komplexität haben: Das gesamte Programm wird in einer einzelnen Datei gespeichert. Dateibasierte Apps eignen sich auch zum Erstellen von Befehlszeilenprogrammen. Auf Unix-Plattformen können dateibasierte Apps mit #! (Shebang)- Direktiven ausgeführt werden. In diesem Tutorial führen Sie Folgendes durch:

  • Erstellen Sie ein dateibasiertes Programm.
  • Unterstützung für Unix-Shebang (#!) hinzufügen.
  • Befehlszeilenargumente lesen.
  • Behandeln von Standardeingaben.
  • Schreiben der ASCII-Grafikausgabe.
  • Verarbeiten von Befehlszeilenargumenten.
  • Verwenden Sie analysierte Befehlszeilenergebnisse.
  • Testen Sie die endgültige Anwendung.

Sie erstellen ein dateibasiertes Programm, das Text als ASCII-Grafik schreibt. Die App ist in einer einzelnen Datei enthalten, verwendet NuGet-Pakete, die einige der Kernfeatures implementieren.

Voraussetzungen

Erstellen eines dateibasierten Programms

  1. Öffnen Sie Visual Studio Code, und erstellen Sie eine neue Datei mit dem Namen AsciiArt.cs. Geben Sie den folgenden Text ein:

    Console.WriteLine("Hello, world!");
    
  2. Speichern Sie die Datei. Öffnen Sie dann das integrierte Terminal in Visual Studio Code, und geben Sie Folgendes ein:

    dotnet run AsciiArt.cs
    

Wenn Sie dieses Programm zum ersten Mal ausführen, erstellt der dotnet Host die ausführbare Datei aus Ihrer Quelldatei, speichert Buildartefakte in einem temporären Ordner und führt dann die erstellte ausführbare Datei aus. Sie können diese Erfahrung überprüfen, indem Sie sie erneut eingeben dotnet run AsciiArt.cs . Dieses Mal bestimmt der dotnet Host, dass die ausführbare Datei aktuell ist, und führt die ausführbare Datei aus, ohne sie erneut zu erstellen. Es wird keine Buildausgabe angezeigt.

Die vorstehenden Schritte veranschaulichen, dass dateibasierte Apps keine Skriptdateien sind. Es handelt sich um C#-Quelldateien, die mithilfe einer generierten Projektdatei in einem temporären Ordner erstellt werden. Eine der Ausgabezeilen, die angezeigt werden, wenn Sie das Programm erstellt haben, sollte etwa wie folgt aussehen (unter Windows):

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

Auf Unix-Plattformen ähnelt der Ausgabeordner folgendem:

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

Diese Ausgabe teilt Ihnen mit, wo die temporären Dateien und Buildausgaben platziert werden. Während dieses Lernprogramms aktualisiert der dotnet Host die ausführbare Datei, wenn Sie die Quelldatei bearbeiten, bevor sie ausgeführt wird.

Dateibasierte Apps sind normale C#-Programme. Die einzige Einschränkung besteht darin, dass sie in einer Quelldatei geschrieben werden müssen. Sie können Anweisungen der obersten Ebene oder eine klassische Main Methode als Einstiegspunkt verwenden. Sie können alle Typen deklarieren: Klassen, Schnittstellen und Strukturen. Sie können die Algorithmen in einem dateibasierten Programm so strukturiert wie in jedem beliebigen C#-Programm. Sie können sogar mehrere Namespaces deklarieren, um Ihren Code zu organisieren. Wenn Sie feststellen, dass ein dateibasiertes Programm für eine einzelne Datei zu groß wird, können Sie es in ein projektbasiertes Programm konvertieren und die Quelle in mehrere Dateien aufteilen. Dateibasierte Apps sind ein hervorragendes Prototyptool. Sie können mit minimalem Aufwand experimentieren, um Konzepte zu beweisen und Algorithmen zu erstellen.

Unix shebang (#!) Unterstützung

Hinweis

Die Unterstützung von #! Direktiven gilt nur für Unix-Plattformen. Es gibt keine ähnliche Direktive für Windows, um ein C#-Programm direkt auszuführen. Unter Windows müssen Sie die Befehlszeile verwenden dotnet run .

Auf Unix können Sie dateibasierte Apps direkt ausführen und den Namen der Quelldatei in die Befehlszeile anstelle von dotnet run. Sie müssen zwei Änderungen vornehmen:

  1. Festlegen von Ausführungsberechtigungen für die Quelldatei:

    chmod +x AsciiArt.cs
    
  2. Fügen Sie eine Shebang-Direktive (#!) als erste Zeile der AsciiArt.cs Datei hinzu:

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

Die Lage kann dotnet auf verschiedenen Unix-Installationen unterschiedlich sein. Verwenden Sie den Befehl which dotnet , um den dotnet Host in Ihrer Umgebung zu finden.

Alternativ können Sie den .NET-Pfad automatisch aus der PATH-Umgebungsvariable ermitteln:

#!/usr/bin/env dotnet

Nachdem Sie diese beiden Änderungen vorgenommen haben, können Sie das Programm direkt über die Befehlszeile ausführen:

./AsciiArt.cs

Wenn Sie es vorziehen, können Sie die Erweiterung entfernen, damit Sie stattdessen eingeben ./AsciiArt können. Sie können die #! Quelldatei auch dann hinzufügen, wenn Sie Windows verwenden. Die Windows-Befehlszeile unterstützt #!nicht, aber der C#-Compiler ermöglicht diese Direktive in dateibasierten Apps auf allen Plattformen.

Befehlszeilenargumente lesen

Schreiben Sie nun alle Argumente in der Befehlszeile in die Ausgabe.

  1. Ersetzen Sie den aktuellen Inhalt durch AsciiArt.cs den folgenden Code:

    if (args.Length > 0)
    {
        string message = string.Join(' ', args);
        Console.WriteLine(message);
    }
    
  2. Sie können diese Version ausführen, indem Sie den folgenden Befehl eingeben:

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

    Die -- Option gibt an, dass alle folgenden Befehlsargumente an das AsciiArt-Programm übergeben werden sollen. Die Argumente This is the command line. werden als Array von Zeichenfolgen übergeben, wobei jede Zeichenfolge ein Wort ist: This, , , is, the, commandund line..

Diese Version veranschaulicht die folgenden neuen Konzepte:

  • Die Befehlszeilenargumente werden mithilfe der vordefinierten Variablen argsan das Programm übergeben. Die args Variable ist ein Array von Zeichenfolgen: string[]. Wenn die Länge args 0 ist, bedeutet dies, dass keine Argumente angegeben wurden. Andernfalls wird jedes Wort in der Argumentliste im entsprechenden Eintrag im Array gespeichert.
  • Die string.Join Methode verknüpft mehrere Zeichenfolgen mit einer einzelnen Zeichenfolge mit dem angegebenen Trennzeichen. In diesem Fall ist das Trennzeichen ein einzelnes Leerzeichen.
  • Console.WriteLine schreibt die Zeichenfolge in die Standardausgabekonsole, gefolgt von einer neuen Zeile.

Behandeln von Standardeingaben

Dadurch werden Befehlszeilenargumente richtig behandelt. Fügen Sie nun den Code hinzu, um das Lesen von Eingaben von Standardeingaben (stdin) anstelle von Befehlszeilenargumenten zu behandeln.

  1. Fügen Sie der Anweisung, die Sie im vorherigen Code hinzugefügt haben, die else folgende if Klausel hinzu:

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

    Der vorherige Code liest die Konsoleneingabe, bis entweder eine leere Zeile oder eine null gelesen wird. (Die Console.ReadLine Methode gibt zurück null , wenn der Eingabedatenstrom durch Eingabe von STRG+C geschlossen wird.)

  2. Testen Sie das Lesen von Standardeingaben, indem Sie eine neue Textdatei im selben Ordner erstellen. Benennen Sie die Datei input.txt , und fügen Sie die folgenden Zeilen hinzu:

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

    Halten Sie die Zeilen kurz, damit sie korrekt formatiert werden, wenn Sie das Feature zur Verwendung von ASCII-Grafiken hinzufügen.

  3. Führen Sie das Programm erneut aus.

    Mit Bash:

    cat input.txt | dotnet run AsciiArt.cs
    

    Oder mit PowerShell:

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

Jetzt kann Ihr Programm entweder Befehlszeilenargumente oder Standardeingaben akzeptieren.

AsciiArt-Ausgabe schreiben

Fügen Sie als Nächstes ein Paket hinzu, das ASCII-Grafiken unterstützt, "Colorful.Console". Wenn Sie einem dateibasierten Programm ein Paket hinzufügen möchten, verwenden Sie die #:package Direktive.

  1. Fügen Sie die folgende Direktive nach der Direktive in der #! datei AsciiArt.cs hinzu:

    #:package Colorful.Console@1.2.15
    

    Von Bedeutung

    Die Version 1.2.15 war die neueste Version des Colorful.Console Pakets, als dieses Lernprogramm zuletzt aktualisiert wurde. Überprüfen Sie die NuGet-Seite des Pakets auf die neueste Version, um sicherzustellen, dass Sie eine Paketversion mit den neuesten Sicherheitsupdates verwenden.

  2. Ändern Sie stattdessen die Zeilen, die aufgerufen Console.WriteLine werden, um die Colorful.Console.WriteAscii Methode zu verwenden:

    async Task WriteAsciiArt(AsciiMessageOptions options)
    {
        foreach (string message in options.Messages)
        {
            Colorful.Console.WriteAscii(message);
            await Task.Delay(options.Delay);
        }
    }
    
  3. Führen Sie das Programm aus, und sie sehen die ASCII-Grafikausgabe anstelle von echoiertem Text.

Prozessbefehlsoptionen

Als Nächstes fügen wir die Befehlszeilenanalyse hinzu. Die aktuelle Version schreibt jedes Wort als eine andere Ausgabezeile. Die Befehlszeilenargumente, die Sie hinzugefügt haben, unterstützen zwei Features:

  1. Anführungszeichen mehrerer Wörter, die in einer Zeile geschrieben werden sollen:

    AsciiArt.cs "This is line one" "This is another line" "This is the last line"
    
  2. Fügen Sie eine --delay Option zum Anhalten zwischen den einzelnen Zeilen hinzu:

    AsciiArt.cs --delay 1000
    

Benutzer sollten beide Argumente zusammen verwenden können.

Die meisten Befehlszeilenanwendungen müssen Befehlszeilenargumente analysieren, um Optionen, Befehle und Benutzereingaben effektiv zu verarbeiten. Die System.CommandLine Bibliothek bietet umfassende Funktionen zum Behandeln von Befehlen, Unterbefehlen, Optionen und Argumenten, sodass Sie sich nicht auf die Mechanismen der Analyse von Befehlszeileneingaben, sondern auf die Funktionsweise Ihrer Anwendung konzentrieren können.

Die System.CommandLine Bibliothek bietet mehrere wichtige Vorteile:

  • Automatische Hilfetextgenerierung und -validierung.
  • Unterstützung für POSIX- und Windows-Befehlszeilenkonventionen.
  • Integrierte Funktionen zum Abschließen von Registerkarten.
  • Einheitliches Analyseverhalten für alle Anwendungen.
  1. Fügen Sie das System.CommandLine Paket hinzu. Fügen Sie diese Direktive nach der vorhandenen Paketdirektive hinzu:

    #:package System.CommandLine@2.0.0
    

    Von Bedeutung

    Die Version war die neueste Version 2.0.0 , als dieses Lernprogramm zuletzt aktualisiert wurde. Wenn eine neuere Version verfügbar ist, verwenden Sie die neueste Version, um sicherzustellen, dass Sie über die neuesten Sicherheitspakete verfügen. Überprüfen Sie die NuGet-Seite des Pakets auf die neueste Version, um sicherzustellen, dass Sie eine Paketversion mit den neuesten Sicherheitsupdates verwenden.

  2. Fügen Sie oben in der Datei die erforderlichen Using-Anweisungen hinzu (nach den #! Anweisungen #:package ):

    using System.CommandLine;
    using System.CommandLine.Parsing;
    
  3. Definieren Sie das Argument "Verzögerungsoption" und "Nachrichten". Fügen Sie den folgenden Code hinzu, um die CommandLine.Option Befehlszeilenoption und CommandLine.Argument das Argument darzustellen:

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

    In Befehlszeilenanwendungen beginnen Optionen in der Regel mit -- (doppelter Gedankenstrich) und können Argumente akzeptieren. Die --delay Option akzeptiert ein ganzzahliges Argument, das die Verzögerung in Millisekunden angibt. Die messagesArgument Definition definiert, wie alle verbleibenden Token nach der Analyse von Optionen als Text analysiert werden. Jedes Token wird zu einer separaten Zeichenfolge im Array, text kann jedoch zitiert werden, um mehrere Wörter in ein Token einzuschließen. Beispielsweise "This is one message" wird ein einzelnes Token, während This is four tokens es zu vier separaten Token wird.

    Der vorangehende Code definiert den Argumenttyp für die --delay Option und dass es sich bei den Argumenten um ein Array von string Werten handelt. Diese Anwendung hat nur einen Befehl, sodass Sie den Stammbefehl verwenden.

  4. Erstellen Sie einen Stammbefehl, und konfigurieren Sie ihn mit der Option und dem Argument. Fügen Sie das Argument und die Option zum Stammbefehl hinzu:

    RootCommand rootCommand = new("Ascii Art file-based program sample");
    
    rootCommand.Options.Add(delayOption);
    rootCommand.Arguments.Add(messagesArgument);
    
  5. Fügen Sie den Code hinzu, um die Befehlszeilenargumente zu analysieren und fehler zu behandeln. Dieser Code überprüft die Befehlszeilenargumente und speichert analysierte Argumente im System.CommandLine.ParseResult Objekt:

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

Der vorangehende Code überprüft alle Befehlszeilenargumente. Wenn die Überprüfung fehlschlägt, werden Fehler in die Konsole geschrieben und die App beendet.

Verwenden analysierter Befehlszeilenergebnisse

Beenden Sie nun die App, um die analysierten Optionen zu verwenden und die Ausgabe zu schreiben. Definieren Sie zunächst einen Datensatz, der die analysierten Optionen enthält. Dateibasierte Apps können Typdeklarationen wie Datensätze und Klassen enthalten. Sie müssen nach allen Anweisungen der obersten Ebene und lokalen Funktionen sein.

  1. Fügen Sie eine record Deklaration hinzu, um die Nachrichten und den Verzögerungsoptionswert zu speichern:

    public record AsciiMessageOptions(string[] Messages, int Delay);
    
  2. Fügen Sie die folgende lokale Funktion vor der Datensatzdeklaration hinzu. Diese Methode behandelt Befehlszeilenargumente und Standardeingaben und gibt eine neue Datensatzinstanz zurück:

    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. Erstellen Sie eine lokale Funktion, um die ASCII-Grafik mit der angegebenen Verzögerung zu schreiben. Diese Funktion schreibt jede Nachricht im Datensatz mit der angegebenen Verzögerung zwischen jeder Nachricht:

    async Task WriteAsciiArt(AsciiMessageOptions options)
    {
        foreach (string message in options.Messages)
        {
            Colorful.Console.WriteAscii(message);
            await Task.Delay(options.Delay);
        }
    }
    
  4. Ersetzen Sie die if klausel, die Sie zuvor geschrieben haben, durch den folgenden Code, der die Befehlszeilenargumente verarbeitet, und schreiben Sie die Ausgabe:

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

Sie haben einen record Typ erstellt, der den analysierten Befehlszeilenoptionen und -argumenten Struktur bereitstellt. Neue lokale Funktionen erstellen eine Instanz des Datensatzes und verwenden den Datensatz, um die ASCII-Grafikausgabe zu schreiben.

Testen der endgültigen Anwendung

Testen Sie die Anwendung, indem Sie mehrere verschiedene Befehle ausführen. Wenn Sie Probleme haben, finden Sie hier das fertige Beispiel, um mit dem zu vergleichen, was Sie erstellt haben:If you have trouble, here's the finished sample to compare with what you built:

#!/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);

In diesem Lernprogramm haben Sie gelernt, ein dateibasiertes Programm zu erstellen, in dem Sie das Programm in einer einzigen C#-Datei erstellen. Diese Programme verwenden keine Projektdatei und können die #! Direktive auf Unix-Systemen verwenden. Lernende können diese Programme nach dem Testen unserer Onlinelernprogramme und vor dem Erstellen größerer projektbasierter Apps erstellen. Dateibasierte Apps sind auch eine großartige Plattform für Befehlszeilenprogramme.