Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
V tomto kurzu se naučíte řadu funkcí v .NET a jazyce C#. Naučíte se:
- Základy rozhraní příkazového řádku .NET
- Struktura konzolové aplikace jazyka C#
- Vstupně-výstupní operace konzoly
- Základy rozhraní API pro vstupně-výstupní operace souborů v .NET
- Základy asynchronního programování založeného na úlohách v .NET
Vytvoříte aplikaci, která přečte textový soubor a vypíše obsah tohoto textového souboru do konzoly. Výstup do konzole je načasován tak, aby odpovídal tempu čtení nahlas. Rychlost můžete zrychlit nebo zpomalit stisknutím kláves "<" (menší než) nebo ">" (větší než). Tuto aplikaci můžete spustit ve Windows, Linuxu, macOS nebo v kontejneru Dockeru.
V tomto tutoriálu je spousta funkcí. Pojďme je sestavit jeden po druhém.
Požadavky
- Nejnovější sada .NET SDK
- editor Visual Studio Code editoru
- C# DevKit
Vytvoření aplikace
Prvním krokem je vytvoření nové aplikace. Otevřete příkazový řádek a vytvořte nový adresář pro vaši aplikaci. Nastavte aktuální adresář. Na příkazovém řádku zadejte příkaz dotnet new console. Například:
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.
Tím se vytvoří počáteční soubory pro základní aplikaci Hello World.
Než začnete s úpravami, spustíme jednoduchou aplikaci Hello World. Po vytvoření aplikace zadejte dotnet run do příkazového řádku. Tento příkaz spustí proces obnovení balíčku NuGet, vytvoří spustitelný soubor aplikace a spustí spustitelný soubor.
Jednoduchý kód aplikace Hello World je v Program.cs. Otevřete tento soubor v oblíbeném textovém editoru. Nahraďte kód v Program.cs následujícím kódem:
namespace TeleprompterConsole;
internal class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
V horní části souboru naleznete namespace výraz. Stejně jako jiné objektově orientované jazyky, které jste mohli použít, jazyk C# používá k uspořádání typů obory názvů. Tento program Hello World se nijak neliší. Vidíte, že program je v oboru názvů s názvem TeleprompterConsole.
Čtení a ozvěna souboru
První funkcí, kterou přidáte, je možnost číst textový soubor a zobrazovat veškerý tento text v konzole. Nejprve přidáme textový soubor. Zkopírujte souborsampleQuotes.txt z úložiště GitHub pro tuto ukázku do adresáře projektu. Bude sloužit jako skript pro vaši aplikaci. Informace o tom, jak stáhnout ukázkovou aplikaci pro tento kurz, najdete v pokynech v ukázkách a kurzech.
Dále do třídy Program přidejte následující metodu (přímo pod metodu Main ):
static IEnumerable<string> ReadFrom(string file)
{
string? line;
using (var reader = File.OpenText(file))
{
while ((line = reader.ReadLine()) != null)
{
yield return line;
}
}
}
Tato metoda je speciálním typem metody jazyka C#, která se nazývá metoda iterátoru. Metody iterátoru vrací sekvence, které jsou vyhodnoceny líným způsobem. To znamená, že každá položka v sekvenci se vygeneruje, protože je požadována kódem, který tuto sekvenci využívá. Metody iterátoru jsou metody, které obsahují jeden nebo více yield return příkazů. Objekt vrácený metodou ReadFrom obsahuje kód pro vygenerování každé položky v posloupnosti. V tomto příkladu to zahrnuje čtení dalšího řádku textu ze zdrojového souboru a vrácení tohoto řetězce. Pokaždé, když volající kód požaduje další položku z sekvence, kód přečte další řádek textu ze souboru a vrátí ho. Když je soubor zcela přečtený, sekvence označuje, že neexistují žádné další položky.
Existují dva elementy syntaxe jazyka C#, které pro vás můžou být nové. Příkaz using v této metodě spravuje vyčištění prostředků. Proměnná, která je inicializována v using příkazu (reader v tomto příkladu), musí implementovat rozhraní IDisposable. Toto rozhraní definuje jednu metodu , Disposekterá by měla být volána při uvolnění prostředku. Kompilátor vygeneruje volání, když provádění dosáhne koncové závorky using příkazu. Kód vygenerovaný kompilátorem zajišťuje, že se prostředek uvolní i v případě, že je vyvolána výjimka z kódu v bloku definovaném příkazem using.
Proměnná reader je definována pomocí klíčového var slova.
var definuje implicitně zadanou místní proměnnou. To znamená, že typ proměnné je určen typem kompilace objektu přiřazeného proměnné. Toto je návratová hodnota z OpenText(String) metody, což je StreamReader objekt.
Teď vyplníme kód pro čtení souboru v Main metodě:
var lines = ReadFrom("sampleQuotes.txt");
foreach (var line in lines)
{
Console.WriteLine(line);
}
Spusťte program (pomocí dotnet run) a zobrazí se každý řádek vytištěný do konzoly.
Přidání prodlev a formátování výstupu
To, co máte, se zobrazuje příliš rychle, abyste si nahlas přečetli. Teď musíte přidat zpoždění ve výstupu. Když začnete, budete vytvářet nějaký základní kód, který umožňuje asynchronní zpracování. Tyto první kroky však budou obsahovat několik antivzorů. Anti-vzory jsou zdůrazněny v komentářích při přidávání kódu a kód se aktualizuje v pozdějších krocích.
V této části je třeba postupovat podle dvou kroků. Nejprve aktualizujete metodu iterátoru tak, aby místo celých řádků vracela jednotlivá slova. To je hotové díky těmto úpravám.
yield return line; Nahraďte příkaz následujícím kódem:
var words = line.Split(' ');
foreach (var word in words)
{
yield return word + " ";
}
yield return Environment.NewLine;
Dále je potřeba upravit způsob, jakým spotřebováváte řádky souboru, a po napsání každého slova přidat zpoždění.
Console.WriteLine(line) Nahraďte příkaz v Main metodě následujícím blokem:
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();
}
Spusťte ukázku a zkontrolujte výstup. Nyní se vytiskne každé jedno slovo, následované zpožděním 200 ms. Zobrazený výstup ale ukazuje některé problémy, protože zdrojový textový soubor má několik řádků, které mají více než 80 znaků bez konce řádku. Může být těžké to číst, když se text posouvá. To je snadné opravit. Budete mít přehled o délce každého řádku a vygenerovat nový řádek vždy, když délka čáry dosáhne určité prahové hodnoty. Deklarujte místní proměnnou po deklaraci words v metodě ReadFrom pro uchování délky řádku:
var lineLength = 0;
Potom za yield return word + " "; příkaz přidejte následující kód (před pravou složenou závorku):
lineLength += word.Length + 1;
if (lineLength > 70)
{
yield return Environment.NewLine;
lineLength = 0;
}
Spusťte ukázku a budete moct nahlas číst podle předem nakonfigurovaného tempa.
Asynchronní úlohy
V tomto posledním kroku přidáte kód pro asynchronní zápis výstupu v jedné úloze a zároveň spustíte další úlohu, která přečte vstup od uživatele, pokud chce zrychlit nebo zpomalit zobrazení textu, nebo úplně zastavit zobrazení textu. Najdete v něm několik kroků a na konci budete mít všechny potřebné aktualizace. Prvním krokem je vytvoření asynchronní návratové Task metody, která představuje kód, který jste zatím vytvořili pro čtení a zobrazení souboru.
Přidejte tuto metodu do Program třídy (pochází z těla metody Main ):
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);
}
}
}
Všimněte si dvou změn. Nejprve tato verze v těle metody používá klíčové slovo Wait() místo toho, aby synchronně čekala na dokončení úkolu voláním await. Abyste to mohli udělat, musíte do podpisu metody přidat async modifikátor. Tato metoda vrátí Task. Všimněte si, že neexistují žádné návratové příkazy, které vrací Task objekt.
Task Místo toho je tento objekt vytvořen kódem, který kompilátor generuje při použití operátoruawait. Můžete si představit, že se tato metoda vrátí, když dosáhne await. Vrácená Task hodnota označuje, že práce nebyla dokončena. Metoda se obnoví, jakmile se dokončí očekávaný úkol. Po dokončení vrácená Task udává, že je dokončeno.
Volající kód může monitorovat vrácenou hodnotu Task, aby určil, kdy bylo dokončeno.
Před voláním await přidejte klíčové slovo ShowTeleprompter.
await ShowTeleprompter();
To vyžaduje, abyste podpis metody změnili na Main :
static async Task Main(string[] args)
Další informace o async Main metodě najdete v části Základy.
Dále je potřeba napsat druhou asynchronní metodu pro čtení z konzoly a sledovat klíče '<' (menší než), '>' (větší než) a "X" nebo "x". Tady je metoda, kterou přidáte pro tento úkol:
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);
}
Tím se vytvoří výraz lambda, který představuje Action delegáta, který přečte klíč z konzoly, a upraví místní proměnnou představující zpoždění, když uživatel stiskne klávesy '<' (menší než) nebo '>' (větší než). Metoda delegáta se dokončí, když uživatel stiskne klávesy X nebo X, které uživateli umožňují kdykoli zastavit zobrazení textu. Tato metoda používá ReadKey() k blokování a čekání na stisknutí klávesy uživatele.
Je čas vytvořit třídu, která dokáže zpracovávat sdílená data mezi těmito dvěma úkoly. Tato třída obsahuje dvě veřejné vlastnosti: zpoždění a příznak Done , který indikuje, že soubor byl zcela přečtený:
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;
}
}
Vytvoření nového souboru; může to být libovolný název končící .cs. Například TelePrompterConfig.cs. Vložte kód třídy TelePrompterConfig, uložte a zavřete. Vložte danou třídu do TeleprompterConsole oboru názvů, jak je znázorněno. Všimněte si, že příkaz using static umožňuje odkazovat na metody Min bez použití uzavřených názvů třídy nebo oboru názvů Max. Příkaz using static importuje metody z jedné třídy. To je na rozdíl od using příkazu bez static, který importuje všechny třídy z jmenného prostoru.
Dále je potřeba aktualizovat metody ShowTeleprompter a GetInput tak, aby používaly nový objekt config. K dokončení této funkce je potřeba vytvořit novou async Task návratovou metodu, která spustí obě tyto úlohy (GetInput a ShowTeleprompter) a také spravuje sdílená data mezi těmito dvěma úkoly. Vytvořte úlohu RunTelePrompter, která spustí oba úkoly a ukončí se, když se dokončí první úloha.
private static async Task RunTeleprompter()
{
var config = new TelePrompterConfig();
var displayTask = ShowTeleprompter(config);
var speedTask = GetInput(config);
await Task.WhenAny(displayTask, speedTask);
}
Novou metodou je zde volání WhenAny(Task[]). Tím se vytvoří Task, který se dokončí, jakmile je dokončen některý z úkolů v seznamu argumentů.
Dále je potřeba aktualizovat jak metodu ShowTeleprompter, tak i metodu GetInput, aby používaly objekt config pro zpoždění. Objekt konfigurace se předává jako parametr těmto metodám. K úplnému nahrazení metod novým kódem použijte příkaz copy/paste. Uvidíte, že kód používá atributy a volá metody z konfiguračního objektu:
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);
}
Teď je potřeba aktualizovat Main tak, aby volalo RunTeleprompter místo ShowTeleprompter:
await RunTeleprompter();
Závěr
V tomto kurzu jste si ukázali řadu funkcí týkajících se jazyka C# a knihoven .NET Core souvisejících s prací v konzolových aplikacích. Na základě těchto znalostí můžete prozkoumat další informace o jazyce a zde představené předměty. Viděli jste základy vstupně-výstupních operací souborů a konzolových operací, blokování a neblokování použití asynchronního programování založeného na úlohách, prohlídku jazyka C# a uspořádání programů v jazyce C# a rozhraní .NET CLI.
Další informace o vstupně-výstupních operacích souborů najdete v tématu Soubor a vstupně-výstupní operace datového proudu. Další informace o asynchronním programovacím modelu použitém v tomto kurzu naleznete v tématu Asynchronní programování založené na úlohách a asynchronní programování.