KonsolenanwendungConsole Application

EinführungIntroduction

In diesem Tutorial lernen Sie verschiedene Features in .NET Core und der Sprache C# kennen.This tutorial teaches you a number of features in .NET Core and the C# language. Es werden die folgenden Themen abgedeckt:You’ll learn:

  • Grundlagen der .NET Core-Befehlszeilenschnittstelle (CLI)The basics of the .NET Core Command Line Interface (CLI)
  • Die Struktur einer C#-KonsolenanwendungThe structure of a C# Console Application
  • Konsolen-E/AConsole I/O
  • Grundlagen der Datei-E/A-APIs in .NET CoreThe basics of File I/O APIs in .NET Core
  • Grundlagen des taskbasierten asynchronen Programmiermodells in .NET CoreThe basics of the Task Asynchronous Programming Model in .NET Core

Sie erstellen eine Anwendung, die eine Textdatei einliest und die Inhalte dieser Textdatei an die Konsole ausgibt.You’ll build an application that reads a text file, and echoes the contents of that text file to the console. Das Tempo der Ausgabe in der Konsole wird so festgelegt, dass ein lautes Mitlesen möglich ist.The output to the console will be paced to match reading it aloud. Sie können die Ausgabe beschleunigen oder verlangsamen, indem Sie die Tasten < oder > drücken.You can speed up or slow down the pace by pressing the ‘<’ or ‘>’ keys.

In diesem Tutorial werden viele Features abgedeckt.There are a lot of features in this tutorial. Gehen wir sie einzeln an.Let’s build them one by one.

Erforderliche KomponentenPrerequisites

Sie müssen Ihren Computer zur Ausführung von .NET Core einrichten.You’ll need to setup your machine to run .NET core. Die Installationsanweisungen finden Sie auf der Seite .NET Core.You can find the installation instructions on the .NET Core page. Sie können diese Anwendung unter Windows, Linux, macOS oder in einem Docker-Container ausführen.You can run this application on Windows, Linux, macOS or in a Docker container. Sie müssen Ihren bevorzugten Code-Editor installieren.You’ll need to install your favorite code editor.

Erstellen der AnwendungCreate the Application

Im ersten Schritt wird eine neue Anwendung erstellt.The first step is to create a new application. Öffnen Sie eine Eingabeaufforderung, und erstellen Sie ein neues Verzeichnis für Ihre Anwendung.Open a command prompt and create a new directory for your application. Legen Sie das Verzeichnis als aktuelles Verzeichnis fest.Make that the current directory. Geben Sie an der Eingabeaufforderung den Befehl dotnet new console ein.Type the command dotnet new console at the command prompt. Hierdurch werden die Startdateien für eine einfache „Hello World“-Anwendung erstellt.This creates the starter files for a basic "Hello World" application.

Bevor Sie damit beginnen, Änderungen durchzuführen, gehen wir die Schritte zur Ausführung der einfachen Hello World-Anwendung durch.Before you start making modifications, let’s go through the steps to run the simple Hello World application. Geben Sie nach dem Erstellen der Anwendung dotnet restore (Siehe Hinweis) an der Eingabeaufforderung.After creating the application, type dotnet restore (see note) at the command prompt. Mit diesem Befehl wird der Prozess zur NuGet-Paketwiederherstellung ausgeführt.This command runs the NuGet package restore process. NuGet ist ein .NET-Paket-Manager.NuGet is a .NET package manager. Mit diesem Befehl werden alle fehlenden abhängigen Komponenten für Ihr Projekt heruntergeladen.This command downloads any of the missing dependencies for your project. Da es sich um ein neues Projekt handelt, ist keine der abhängigen Komponenten vorhanden, deshalb wird zunächst das .NET Core-Framework heruntergeladen.As this is a new project, none of the dependencies are in place, so the first run will download the .NET Core framework. Nach diesem ersten Schritt, Sie müssen nur auszuführende dotnet restore (Siehe Hinweis) Wenn Sie neue abhängigen Pakete hinzufügen oder aktualisieren Sie die Versionen eine Ihrer Abhängigkeiten.After this initial step, you will only need to run dotnet restore (see note) when you add new dependent packages, or update the versions of any of your dependencies. Bei diesem Vorgang wird auch die Projektsperrdatei (project.lock.json) in Ihrem Projektverzeichnis erstellt.This process also creates the project lock file (project.lock.json) in your project directory. Diese Datei bietet Unterstützung beim Verwalten der Projektabhängigkeiten.This file helps to manage the project dependencies. Sie enthält den lokalen Speicherort für alle Projektabhängigkeiten.It contains the local location of all the project dependencies. Sie müssen nicht die Datei im Datenquellen-Steuerelements abgelegt. Es wird generiert, wenn Sie ausführen dotnet restore (Siehe Hinweis).You do not need to put the file in source control; it will be generated when you run dotnet restore (see note).

Nach dem Wiederherstellen der Pakete führen Sie dotnet build aus.After restoring packages, you run dotnet build. Hiermit wird das Buildmodul ausgeführt und die ausführbare Datei für Ihre Anwendung erstellt.This executes the build engine and creates your application executable. Abschließend führen Sie dotnet run aus, um Ihre Anwendung zu starten.Finally, you execute dotnet run to run your application.

Der gesamte Code für die einfache Hello World-Anwendung ist in „Program.cs“ enthalten.The simple Hello World application code is all in Program.cs. Öffnen Sie diese Datei mit Ihrem bevorzugten Text-Editor.Open that file with your favorite text editor. Wir werden jetzt die ersten Änderungen vornehmen.We’re about to make our first changes. Am Anfang der Datei sehen Sie eine using-Anweisung:At the top of the file, see a using statement:

using System;

Diese Anweisung informiert den Compiler, dass alle Typen aus dem System-Namespace im Gültigkeitsbereich liegen.This statement tells the compiler that any types from the System namespace are in scope. Wie andere objektorientierte Sprachen, die Sie vielleicht schon verwendet haben, verwendet C# Namespaces, um Typen zu organisieren.Like other Object Oriented languages you may have used, C# uses namespaces to organize types. Dieses Hello World-Programm funktioniert genauso.This hello world program is no different. Sie können sehen, dass das Programm im ConsoleApplication-Namespace enthalten ist.You can see that the program is enclosed in the ConsoleApplication namespace. Dies ist kein besonders aussagekräftiger Name, ändern Sie ihn deshalb in TeleprompterConsole:That’s not a very descriptive name, so change it to TeleprompterConsole:

namespace TeleprompterConsole

Lesen und Ausgeben der DateiReading and Echoing the File

Das erste hinzuzufügende Feature ist die Möglichkeit zum Lesen einer Textdatei und zum Anzeigen des gesamten Texts in der Konsole.The first feature to add is the ability to read a text file and display all that text to the console. Fügen wir zunächst eine Textdatei hinzu.First, let’s add a text file. Kopieren Sie die Datei sampleQuotes.txt aus dem GitHub-Repository für dieses Beispiel in Ihr Projektverzeichnis.Copy the sampleQuotes.txt file from the GitHub repository for this sample into your project directory. Diese Datei dient als Skript für Ihre Anwendung.This will serve as the script for your application. Wenn Sie Informationen dazu erhalten möchten, wie Sie die Beispiel-App für dieses Thema herunterladen, finden Sie Anweisungen im Thema Beispiele und Tutorials.If you would like information on how to download the sample app for this topic, see the instructions in the Samples and Tutorials topic.

Fügen Sie als Nächstes die folgenden Methoden in Ihre Program-Klasse ein (rechts neben der Main-Methode):Next, add the following method in your Program class (right below the Main method):

static IEnumerable<string> ReadFrom(string file)
{
    string line;
    using (var reader = File.OpenText(file))
    {
        while ((line = reader.ReadLine()) != null)
        {
            yield return line;
        }
    }
}

Diese Methode verwendet Typen aus zwei neuen Namespaces.This method uses types from two new namespaces. Für die Kompilierung müssen Sie oben in der Datei die folgenden zwei Zeilen einfügen:For this to compile you’ll need to add the following two lines to the top of the file:

using System.Collections.Generic;
using System.IO;

Die IEnumerable<T>-Schnittstelle ist im System.Collections.Generic-Namespace definiert.The IEnumerable<T> interface is defined in the System.Collections.Generic namespace. Die File-Klasse ist im System.IO-Namespace definiert.The File class is defined in the System.IO namespace.

Diese Methode ist ein besonderer Typ von C#-Methode, der als Enumeratormethode bezeichnet wird.This method is a special type of C# method called an Enumerator method. Enumeratormethoden geben Sequenzen zurück, die verzögert ausgewertet werden.Enumerator methods return sequences that are evaluated lazily. Dies bedeutet, dass jedes Element in der Sequenz dann generiert wird, wenn es vom Code angefordert wird, der die Sequenz verarbeitet.That means each item in the sequence is generated as it is requested by the code consuming the sequence. Enumeratormethoden sind Methoden, die mindestens eine yield return-Anweisung enthalten.Enumerator methods are methods that contain one or more yield return statements. Das von der ReadFrom-Methode zurückgegebene Objekt enthält den Code zum Generieren aller Elemente in der Sequenz.The object returned by the ReadFrom method contains the code to generate each item in the sequence. In diesem Beispiel umfasst dies das Einlesen der nächsten Textzeile aus der Quelldatei und die Rückgabe dieser Zeichenfolge.In this example, that involves reading the next line of text from the source file, and returning that string. Jedes Mal, wenn der aufrufende Code die nächste Zeile aus der Sequenz anfordert, liest der Code die nächste Zeile aus der Textdatei und gibt sie zurück.Each time the calling code requests the next item from the sequence, the code reads the next line of text from the file and returns it. Wenn die Datei vollständig gelesen wurde, gibt die Sequenz an, dass keine weiteren Elemente vorhanden sind.When the file has been completely read, the sequence indicates that there are no more items.

Es gibt zwei weitere C#-Syntaxelemente, die möglicherweise neu für Sie sind.There are two other C# syntax elements that may be new to you. Die using-Anweisung in dieser Methode verwaltet die Ressourcenbereinigung.The using statement in this method manages resource cleanup. Die Variable, die in der using-Anweisung initialisiert wird – in diesem Beispiel reader –, muss die IDisposable-Schnittstelle implementieren.The variable that is initialized in the using statement (reader, in this example) must implement the IDisposable interface. Die IDisposable-Schnittstelle definiert eine einzige Methode, Dispose, die aufgerufen werden muss, wenn die Ressource freigegeben werden soll.The IDisposable interface defines a single method, Dispose, that should be called when the resource should be released. Der Compiler generiert diesen Aufruf, wenn die Ausführung die schließende geschweifte Klammer der using-Anweisung erreicht.The compiler generates that call when execution reaches the closing brace of the using statement. Der vom Compiler generierte Code stellt sicher, dass die Ressource selbst dann freigegeben wird, wenn der Code im durch die using-Anweisung definierten Block eine Ausnahme auslöst.The compiler-generated code ensures that the resource is released even if an exception is thrown from the code in the block defined by the using statement.

Die reader-Variable wird mit dem var-Schlüsselwort definiert.The reader variable is defined using the var keyword. var definiert eine implizit typisierte lokale Variable.var defines an implicitly typed local variable. Dies bedeutet, dass der Typ der Variablen durch den Kompilierzeittyp des Objekts bestimmt wird, das der Variablen zugewiesen ist.That means the type of the variable is determined by the compile time type of the object assigned to the variable. Hier können, die den Rückgabewert ist der OpenText(String) Methode, die eine StreamReader Objekt.Here, that is the return value from the OpenText(String) method, which is a StreamReader object.

Füllen wir jetzt den Code in der Main-Methode, um die Datei zu lesen:Now, let’s fill in the code to read the file in the Main method:

var lines = ReadFrom("sampleQuotes.txt");
foreach (var line in lines)
{
    Console.WriteLine(line); 
}

Führen Sie das Programm aus (Sie verwenden hierzu dotnet run, und jede Zeile wird einzeln an der Konsole ausgegeben).Run the program (using dotnet run and you can see every line printed out to the console).

Hinzufügen von Verzögerungen und Formatieren der AusgabeAdding Delays and Formatting output

Der Text wird momentan zu schnell ausgegeben, um ihn laut mitzulesen.What you have is being displayed far too fast to read aloud. Jetzt müssen Sie Verzögerungen in der Ausgabe hinzufügen.Now you need to add the delays in the output. Zu Beginn erstellen Sie einen Teil des grundlegenden Codes, der eine asynchrone Verarbeitung ermöglicht.As you start, you’ll be building some of the core code that enables asynchronous processing. Auf diese ersten Schritte folgen jedoch einige Antimuster.However, these first steps will follow a few anti-patterns. Die Antimuster werden in Kommentaren erläutert, die Sie im Code hinzufügen, und der Code wird in späteren Schritten aktualisiert.The anti-patterns are pointed out in comments as you add the code, and the code will be updated in later steps.

Dieser Abschnitt umfasst zwei Schritte.There are two steps to this section. Zunächst aktualisieren Sie die Iteratormethode, um anstelle von ganzen Zeilen einzelne Wörter zurückzugeben.First, you’ll update the iterator method to return single words instead of entire lines. Dies wird durch diese Änderungen erreicht.That’s done with these modifications. Ersetzen Sie die yield return line;-Anweisung durch den folgenden Code:Replace the yield return line; statement with the following code:

var words = line.Split(' ');
foreach (var word in words)
{
    yield return word + " ";
}
yield return Environment.NewLine;

Als Nächstes ändern Sie die Art und Weise, in der die Dateizeilen verarbeitet werden und fügen nach jedem geschriebenen Wort eine Verzögerung hinzu.Next, you need to modify how you consume the lines of the file, and add a delay after writing each word. Ersetzen Sie die Console.WriteLine(line)-Anweisung in der Main-Methode durch den folgenden Block:Replace the Console.WriteLine(line) statement in the Main method with the following block:

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();
}

Die Task-Klasse ist im System.Threading.Tasks-Namespace enthalten, deshalb müssen Sie diese using-Anweisung am Anfang der Datei hinzufügen:The Task class is in the System.Threading.Tasks namespace, so you need to add that using statement at the top of file:

using System.Threading.Tasks;

Führen Sie das Beispiel aus, und überprüfen Sie die Ausgabe.Run the sample, and check the output. Jetzt folgt nach der Ausgabe jedes Worts eine Pause von 200 ms.Now, each single word is printed, followed by a 200 ms delay. Die angezeigte Ausgabe ist jedoch fehlerhaft, weil der Quelltext mehrere Zeilen umfasst, die mehr als 80 Zeichen ohne Zeilenumbruch aufweisen.However, the displayed output shows some issues because the source text file has several lines that have more than 80 characters without a line break. Der Text ist möglicherweise schwer zu lesen, wenn er ohne Umbrüche angezeigt wird.That can be hard to read while it's scrolling by. Dieses Problem kann einfach gelöst werden.That’s easy to fix. Sie verfolgen einfach die Länge jeder Zeile nach und generieren immer dann eine neue Zeile, wenn die Zeilenlänge einen bestimmten Schwellenwert überschreitet.You’ll just keep track of the length of each line, and generate a new line whenever the line length reaches a certain threshold. Deklarieren Sie nach der Deklaration von words eine lokale Variable, die die Zeilenlänge enthält:Declare a local variable after the declaration of words that holds the line length:

var lineLength = 0;

Fügen Sie dann nach der yield return word + " ";-Anweisung (vor der schließenden geschweiften Klammer) den folgenden Code hinzu:Then, add the following code after the yield return word + " "; statement (before the closing brace):

lineLength += word.Length + 1;
if (lineLength > 70)
{
    yield return Environment.NewLine;
    lineLength = 0;
}

Führen Sie das Beispiel aus. Jetzt sollten Sie in der Lage sein, den Text im festgelegten Tempo laut mitzulesen.Run the sample, and you’ll be able to read aloud at its pre-configured pace.

Asynchrone TasksAsync Tasks

Zuletzt fügen Sie den Code hinzu, mit dem in einem Task die Ausgabe asynchron geschrieben wird, während in einem weiteren Task Eingaben vom Benutzer gelesen werden, um ggf. die Geschwindigkeit der Textanzeige zu erhöhen oder zu verringern.In this final step, you’ll add the code to write the output asynchronously in one task, while also running another task to read input from the user if they want to speed up or slow down the text display. Hierzu sind einige Schritte erforderlich, damit Sie am Ende über alle benötigten Aktualisierungen verfügen.This has a few steps in it and by the end, you’ll have all the updates that you need. Im ersten Schritt erstellen Sie eine asynchrone Task-Rückgabemethode, die den Code repräsentiert, den Sie bisher zum Lesen und Anzeigen der Datei erstellt haben.The first step is to create an asynchronous Task returning method that represents the code you’ve created so far to read and display the file.

Fügen Sie diese Methode zu Ihrer Program-Klasse hinzu (diese stammt aus dem Körper Ihrer Main-Methode):Add this method to your Program class (it’s taken from the body of your Main method):

private static async Task ShowTeleprompter()
{
    var words = ReadFrom("sampleQuotes.txt");
    foreach (var line in words)
    {
        Console.Write(line);
        if (!string.IsNullOrWhiteSpace(line))
        {
            await Task.Delay(200);
        }
    }
}

Sie werden zwei Änderungen bemerken.You’ll notice two changes. Zunächst wird im Methodenkörper anstelle eines Aufrufs von Wait() zum synchronen Warten auf eine Taskbeendigung in dieser Version das Schlüsselwort await verwendet.First, in the body of the method, instead of calling Wait() to synchronously wait for a task to finish, this version uses the await keyword. Hierzu müssen Sie der Methodensignatur den async-Modifizierer hinzufügen.In order to do that, you need to add the async modifier to the method signature. Diese Methode gibt einen Task zurück.This method returns a Task. Beachten Sie, dass es keine return-Anweisungen gibt, die ein Task-Objekt zurückgeben.Notice that there are no return statements that return a Task object. Stattdessen wird dieses Task-Objekt durch Code erstellt, den der Compiler beim Verwenden des await-Operators generiert.Instead, that Task object is created by code the compiler generates when you use the await operator. Sie können sich dies so vorstellen, dass die Methode eine Rückgabe durchführt, wenn sie ein await-Schlüsselwort erreicht.You can imagine that this method returns when it reaches an await. Der zurückgegebene Task gibt an, dass der Vorgang noch nicht abgeschlossen wurde.The returned Task indicates that the work has not completed. Die Methode wird fortgesetzt, wenn der erwartete Task abgeschlossen ist.The method resumes when the awaited task completes. Nach Abschluss der Ausführung weist der zurückgegebene Task darauf hin, dass er abgeschlossen wurde.When it has executed to completion, the returned Task indicates that it is complete. Der aufrufende Code kann den zurückgegebenen Task überwachen, um zu ermitteln, wann dieser abgeschlossen ist.Calling code can monitor that returned Task to determine when it has completed.

Sie können diese neue Methode in Ihrer Main-Methode aufrufen:You can call this new method in your Main method:

ShowTeleprompter().Wait();

Hier führt der Code in Main einen asynchronen Wartevorgang aus.Here, in Main, the code does synchronously wait. Sie sollten nach Möglichkeit anstelle eines synchronen Wartevorgangs immer den await-Operator verwenden.You should use the await operator instead of synchronously waiting whenever possible. In der Main-Methode einer Konsolenanwendung kann der await-Operator jedoch nicht verwendet werden.But, in a console application’s Main method, you cannot use the await operator. Dies würde dazu führen, dass die Anwendung beendet wird, bevor alle Tasks abgeschlossen sind.That would result in the application exiting before all tasks have completed.

Als Nächstes müssen Sie die zweite asynchrone Methode schreiben, um Inhalte aus der Konsole zu lesen und auf die Tasteneingaben < und > zu überwachen.Next, you need to write the second asynchronous method to read from the Console and watch for the ‘<’ and ‘>’ keys. Hier ist die Methode, die Sie für diesen Task hinzufügen:Here’s the method you add for that task:

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;
            }
        } while (true);
    };
    await Task.Run(work);
}

Hiermit wird ein Lambda-Ausdruck zur Darstellung eines Action-Delegaten erstellt. Mit diesem wird ein Schlüssel aus der Konsole gelesen und eine lokale Variable geändert, die die Verzögerung beim Drücken der Tasten < oder > durch den Benutzer repräsentiert.This creates a lambda expression to represent an Action delegate that reads a key from the Console and modifies a local variable representing the delay when the user presses the ‘<’ or ‘>’ keys. Diese Methode verwendet ReadKey() zum Blockieren und wartet darauf, dass der Benutzer eine Taste drückt.This method uses ReadKey() to block and wait for the user to press a key.

Um dieses Feature abzuschließen, müssen Sie eine neue async Task-Rückgabemethode erstellen, die beide Tasks (GetInput und ShowTeleprompter) startet und außerdem die von diesen Tasks gemeinsam verwendeten Daten verwaltet.To finish this feature, you need to create a new async Task returning method that starts both of these tasks (GetInput and ShowTeleprompter), and also manages the shared data between these two tasks.

Es muss eine neue Klasse erstellt werden, mit der die von diesen zwei Tasks gemeinsam verwendeten Daten verarbeitet werden können.It’s time to create a class that can handle the shared data between these two tasks. Diese Klasse enthält zwei öffentliche Eigenschaften: die Verzögerung und ein Flag, mit dem angegeben wird, dass die Datei vollständig gelesen wurde:This class contains two public properties: the delay, and a flag to indicate that the file has been completely read:

namespace TeleprompterConsole
{
    internal class TelePrompterConfig
    {
        private object lockHandle = new object();
        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);
            lock (lockHandle)
            {
                DelayInMilliseconds = newDelay;
            }
        }
    }
}

Platzieren Sie diese Klasse in einer neuen Datei, und fügen Sie diese Klasse in den TeleprompterConsole-Namespace ein (wie oben gezeigt).Put that class in a new file, and enclose that class in the TeleprompterConsole namespace as shown above. Sie benötigen außerdem eine using static-Anweisung, damit Sie ohne die Namen der übergeordneten Klasse oder des Namespace auf die Min- und Max-Methode verweisen können.You’ll also need to add a using static statement so that you can reference the Min and Max method without the enclosing class or namespace names. Eine using static-Anweisung importiert die Methoden einer Klasse.A using static statement imports the methods from one class. Dies steht in Kontrast zu den bisher verwendeten using-Anweisungen, die alle Klassen aus einem Namespace importiert haben.This is in contrast with the using statements used up to this point that have imported all classes from a namespace.

using static System.Math;

Ein weiteres neues Sprachfeature ist die lock-Anweisung.The other language feature that’s new is the lock statement. Mit dieser Anweisung wird sichergestellt, dass zu jedem Zeitpunkt nur ein einziger Thread in diesem Codeabschnitt vorhanden sein kann.This statement ensures that only a single thread can be in that code at any given time. Wenn sich ein Thread im gesperrten Abschnitt befindet, müssen andere Threads warten, bis der erste Thread diesen Abschnitt verlässt.If one thread is in the locked section, other threads must wait for the first thread to exit that section. Die lock-Anweisung verwendet ein Objekt, das den gesperrten Abschnitt schützt.The lock statement uses an object that guards the lock section. Diese Klasse verwendet ein Standardidiom, um ein privates Objekt in der Klasse zu sperren.This class follows a standard idiom to lock a private object in the class.

Im nächsten Schritt müssen Sie die ShowTeleprompter- und GetInput-Methoden zur Verwendung des neuen config-Objekts aktualisieren.Next, you need to update the ShowTeleprompter and GetInput methods to use the new config object. Schreiben Sie einen finalen Task, der die async-Methode zurückgibt, um beide Tasks zu starten und den Vorgang zu beenden, sobald der erste Task beendet wird:Write one final Task returning async method to start both tasks and exit when the first task finishes:

private static async Task RunTeleprompter()
{
    var config = new TelePrompterConfig();
    var displayTask = ShowTeleprompter(config);

    var speedTask = GetInput(config);
    await Task.WhenAny(displayTask, speedTask);
}

Eine neue Methode hier besteht die WhenAny(Task[]) aufrufen.The one new method here is the WhenAny(Task[]) call. Hiermit wird ein Task erstellt, der abgeschlossen wird, sobald einer der Tasks in dieser Argumentliste beendet ist.That creates a Task that finishes as soon as any of the tasks in its argument list completes.

Im nächsten Schritt müssen Sie die ShowTeleprompter- und GetInput-Methoden zur Verwendung des neuen config-Objekts aktualisieren:Next, you need to update both the ShowTeleprompter and GetInput methods to use the config object for the delay:

private static async Task ShowTeleprompter(TelePrompterConfig config)
{
    var words = ReadFrom("sampleQuotes.txt");
    foreach (var line in words)
    {
        Console.Write(line);
        if (!string.IsNullOrWhiteSpace(line))
        {
            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);
        } while (!config.Done);
    };
    await Task.Run(work);
}

Diese neue Version von ShowTeleprompter ruft eine neue Methode in der TeleprompterConfig-Klasse auf.This new version of ShowTeleprompter calls a new method in the TeleprompterConfig class. Jetzt müssen Sie Main aktualisieren, um anstelle von ShowTeleprompter RunTeleprompter aufzurufen:Now, you need to update Main to call RunTeleprompter instead of ShowTeleprompter:

RunTeleprompter().Wait();

Zum Abschluss müssen Sie die SetDone-Methode hinzufügen und die Done-Eigenschaft in die TelePrompterConfig-Klasse einfügen:To finish, you'll need to add the SetDone method, and the Done property to the TelePrompterConfig class:

public bool Done => done;

private bool done;

public void SetDone()
{
    done = true;    
}

SchlussbemerkungConclusion

In diesem Tutorial wurden verschiedene Features von C# und den .NET Core-Bibliotheken vorgestellt, die bei der Arbeit in Konsolenanwendungen benötigt werden.This tutorial showed you a number of the features around the C# language and the .NET Core libraries related to working in Console applications. Sie können auf diesem Wissen aufbauen, um C# und die hier beschriebenen Klassen weiter zu erkunden.You can build on this knowledge to explore more about the language, and the classes introduced here. Sie haben die Grundlagen der Datei- und Konsolen-E/A kennengelernt, und es wurden die blockierende und die nicht blockierende Verwendung des taskbasierten asynchronen Programmiermodells vorgestellt. Außerdem haben Sie einen Überblick über die Sprache C# und die Struktur von C#-Programmen erhalten, und Sie haben die .NET Core-Befehlszeilenschnittstelle (CLI) und andere Tools kennengelernt.You’ve seen the basics of File and Console I/O, blocking and non-blocking use of the Task based Asynchronous programming model, a tour of the C# language and how C# programs are organized and the .NET Core Command Line Interface and tools.

Hinweis

Starting with .NET Core 2.0, you don't have to run dotnet restore because it's run implicitly by all commands, such as dotnet build and dotnet run, that require a restore to occur. It's still a valid command in certain scenarios where doing an explicit restore makes sense, such as continuous integration builds in Visual Studio Team Services or in build systems that need to explicitly control the time at which the restore occurs.