Konzolová aplikace
V tomto kurzu se naučíte řadu funkcí v .NET a jazyce C#. Naučíte se:
- Základy .NET CLI
- Struktura konzolové aplikace jazyka C#
- V/V konzoly
- Základy rozhraní API pro V/V soubory v .NET
- Základy asynchronního programování založeného na úlohách v .NET
Sestavíte aplikaci, která přečte textový soubor a vyjádí obsah tohoto textového souboru do konzoly. Výstup do konzoly se předá tak, aby odpovídal jeho čtení. Tempa můžete zrychlit nebo zpomalit stisknutím kláves < (menší než) nebo > (větší než). Tuto aplikaci můžete spustit v Windows, Linuxu, macOS nebo v kontejneru Dockeru.
V tomto kurzu je k dispozici mnoho funkcí. Pojďme je vytvořit jeden po jednom.
Požadavky
- .NET 6 SDK.
- Editor kódu.
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. Ujistěte se, že je aktuální adresář. Na příkazovém dotnet new console řádku zadejte příkaz . Tím se vytvoří počáteční soubory pro základní aplikaci "Hello World".
Než začnete provádět úpravy, pojďme spustit jednoduchou Hello World aplikace. Po vytvoření aplikace na dotnet run příkazovém řádku zadejte . Tento příkaz spustí NuGet balíčku, vytvoří spustitelný soubor aplikace a spustí spustitelný soubor.
Jednoduchým Hello World aplikace je soubor Program.cs. Otevřete tento soubor pomocí oblíbeného textového editoru. Nahraďte kód v souboru 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 se podívejte na namespace příkaz . Podobně jako jiné objektově orientované jazyky, které jste použili, používá jazyk C# k uspořádání typů obory názvů. Tento Hello World se neliší. Vidíte, že program je v oboru názvů s názvem TeleprompterConsole .
Čtení a zobrazení souboru
První funkcí, kterou je třeba přidat, je možnost čtení textového souboru a zobrazení veškerého tohoto textu v konzole. Nejprve přidáme textový soubor. Zkopírujte sampleQuotes.txt z úložiště GitHub pro tuto ukázku do adresáře projektu. To 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 tématu Ukázky a kurzy.
Dále přidejte následující metodu do Program třídy (přímo pod Main metodu ):
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í typ metody jazyka C# nazývaný metoda iterátoru. Metody enumerátoru vracejí líné sekvence. To znamená, že každá položka v sekvenci se vygeneruje, protože ji požaduje kód, který sekvenci používá. Metody enumerá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 sekvenci. V tomto příkladu to zahrnuje přečtení dalšího řádku textu ze zdrojového souboru a vrácení tohoto řetězce. Pokaždé, když volající kód vyžádá další položku ze sekvence, kód přečte další řádek textu ze souboru a vrátí ho. Když je soubor zcela přečten, sekvence indikuje, že neexistují žádné další položky.
Existují dva prvky syntaxe jazyka C#, které mohou být pro vás 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 ( v tomto reader příkladu), musí implementovat IDisposable rozhraní . Toto rozhraní definuje jednu metodu , která by Dispose měla být volána při uvolnění prostředku. Kompilátor generuje toto volání, když provádění dosáhne uzavírací složené závorky using příkazu. Kód generovaný kompilátorem zajišťuje uvolnění prostředku i v případě, že je z kódu v bloku definovaném příkazem using vyvolána výjimka.
Proměnná reader je definována pomocí var klíčového slova . var definuje implicitně typované místní proměnné. To znamená, že typ proměnné je určen typem objektu přiřazeného k proměnné v době kompilace. Tady je to návratová hodnota z OpenText(String) metody , což je objekt StreamReader .
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í příkazu ) a zobrazí se každý dotnet run řádek, který se vytiskne do konzoly.
Přidání zpoždění a formátování výstupu
To, co máte, se zobrazuje příliš rychle nahlas. Teď musíte do výstupu přidat zpoždění. Na začátku budete budovat část základního kódu, který umožňuje asynchronní zpracování. Tyto první kroky však budou dodržovat několik antivzory. Při přidávání kódu jsou antivzory uvedené v komentářích a kód se aktualizuje v dalších krocích.
V této části jsou dva kroky. Nejprve aktualizujete metodu iterátoru tak, aby místo celých řádků vracel jednotlivá slova. To se dělá s těmito úpravami. Nahraďte yield return line; 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 musíte upravit způsob, jakým budete používat řádky souboru, a přidat zpoždění po zápisu každého slova. Nahraďte Console.WriteLine(line) 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. Teď se vytiskne každé jedno slovo následované zpožděním 200 ms. Zobrazený výstup však ukazuje některé problémy, protože zdrojový textový soubor obsahuje několik řádků, které mají více než 80 znaků bez zalomení řádku. To může být při posouvání obtížné přečíst. To se snadno opraví. Budete sledovat délku každého řádku a generovat nový řádek vždy, když délka čáry dosáhne určité prahové hodnoty. Deklarujte místní proměnnou za deklarací v metodě words , která obsahuje délku ReadFrom řádku:
var lineLength = 0;
Potom za příkaz přidejte následující kód yield return word + " "; (před uzavírací 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 její předem nakonfigurované tempo.
Asynchronní úlohy
V tomto posledním kroku přidáte kód pro asynchronní zápis výstupu do jednoho úkolu a zároveň po spuštění další úlohy pro čtení vstupu od uživatele, pokud chce zrychlit nebo zpomalovat textové zobrazení nebo zcela zastavit zobrazení textu. Obsahuje několik kroků a na konci budete mít všechny potřebné aktualizace. Prvním krokem je vytvoření asynchronní návratové metody, která představuje kód, který jste dosud vytvořili pro čtení a Task zobrazení souboru.
Přidejte tuto Program metodu do třídy (je převzatá z těla vaší Main metody):
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 v těle metody namísto volání metody synchronně čekat na dokončení úlohy tato verze používá Wait() klíčové await slovo . K tomu je potřeba přidat async modifikátor do signatury metody. Tato metoda vrátí Task . Všimněte si, že neexistují žádné návratové příkazy, které vracejí Task objekt. Místo toho je Task tento objekt vytvořen kódem, který kompilátor vygeneruje při použití await operátoru . Dokážete si představit, že tato metoda vrátí , když dosáhne await . Vrácená Task hodnota znamená, že se práce neskončila. Metoda se obnoví po dokončení čekající úlohy. Když se provede do dokončení, vrácená hodnota Task indikuje, že je dokončená.
Volání kódu může monitorovat vrácený Task kód a určit, kdy byl dokončen.
Tuto novou metodu můžete volat v Main metodě :
ShowTeleprompter().Wait();
Tady v Main souboru kód synchronně čeká. Pokud je to await možné, měli byste místo synchronního čekání použít operátor . V metodě konzolové aplikace však Main nelze použít operátor await . To by mělo za následek ukončení aplikace před dokončením všech úkolů.
Poznámka
Pokud používáte C# 7.1 nebo novější, můžete vytvořit konzolové aplikace pomocí async Main metody.
Dále musíte 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 pro tuto úlohu přidáte:
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ý reprezentuje delegáta, který čte klíč z konzoly a upravuje místní proměnnou představující zpoždění, když uživatel stiskne klávesu Action < (menší než) nebo > (větší než). Metoda delegáta se dokončí, když uživatel stiskne klávesy X nebo x, což uživateli umožní kdykoli zastavit zobrazení textu. Tato metoda používá ReadKey() k blokování a čekání na stisknutí klávesy uživatelem.
Pokud chcete tuto funkci dokončit, musíte vytvořit novou návratovou metodu, která spustí obě tyto úlohy ( a ) a také spravuje async Task GetInput ShowTeleprompter sdílená data mezi těmito dvěma úkoly.
Je čas vytvořit třídu, která dokáže zpracovat sdílená data mezi těmito dvěma úkoly. Tato třída obsahuje dvě veřejné vlastnosti: zpoždění a příznak, který označuje, že soubor Done byl zcela přečten:
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;
}
}
Umístěte třídu do nového souboru a zahrpište ji do oboru TeleprompterConsole názvů , jak je znázorněno níže. Na začátek souboru budete také muset přidat příkaz , abyste mohli odkazovat na metody a bez názvů nadřazené třídy using static Min nebo oboru Max názvů. Příkaz using static importuje metody z jedné třídy. To je na rozdíl od příkazu using bez static , který importuje všechny třídy z oboru názvů.
using static System.Math;
Dále je potřeba aktualizovat metody a ShowTeleprompter GetInput tak, aby se nový config objekt používat. Napište jednu Task konečnou async návratovou metodu, která spustí úkoly a ukončí se po dokončení prvního úkolu:
private static async Task RunTeleprompter()
{
var config = new TelePrompterConfig();
var displayTask = ShowTeleprompter(config);
var speedTask = GetInput(config);
await Task.WhenAny(displayTask, speedTask);
}
Jednou z nových metod je WhenAny(Task[]) volání . Tím se vytvoří , která se dokončí, jakmile se dokončí kterýkoli z úkolů v Task seznamu argumentů.
Dále je potřeba aktualizovat obě metody ShowTeleprompter GetInput a tak, aby se objekt pro config zpoždění používá:
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);
}
Tato nová verze ShowTeleprompter volá novou metodu ve TeleprompterConfig třídě . Teď je potřeba aktualizovat a Main volat RunTeleprompter místo ShowTeleprompter :
RunTeleprompter().Wait();
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ých třídách. Prohlédněte si základy V/V operací se soubory a konzolou, blokující a neblokující 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í příkazového řádku .NET CLI.
Další informace o V/V souboru najdete v tématu V/V soubor a datový proud. Další informace o asynchronním programovacím modelu použitém v tomto kurzu najdete v tématu Asynchronní programování založené na úlohách a Asynchronní programování.