Megosztás a következőn keresztül:


Konzolalkalmazás

Ez az oktatóanyag a .NET és a C# nyelv számos funkcióját ismerteti. Az oktatóanyagból a következőket sajátíthatja el:

  • A .NET parancssori felület alapjai
  • A C#-konzolalkalmazás felépítése
  • Konzol bemenet/kimenet
  • A file I/O API-k alapjai a .NET-ben
  • A feladatalapú aszinkron programozás alapjai a .NET-ben

Egy olyan alkalmazást fog létrehozni, amely beolvassa a szövegfájlt, és visszhangozza a szövegfájl tartalmát a konzolon. A konzol kimenete úgy van időzítve, hogy megfeleljen a hangos olvasás tempójának. A '<' (kisebb, mint) vagy '>' (nagyobb mint) billentyű lenyomásával felgyorsíthatja vagy lelassíthatja a tempót. Ezt az alkalmazást Windows, Linux, macOS vagy Docker-tárolóban is futtathatja.

Ebben az oktatóanyagban számos funkció található. Építsük fel őket egyenként.

Előfeltételek

Az alkalmazás létrehozása

Az első lépés egy új alkalmazás létrehozása. Nyisson meg egy parancssort, és hozzon létre egy új könyvtárat az alkalmazás számára. Állítsa be azt a jelenlegi könyvtárként. Írja be a parancsot dotnet new console a parancssorba. Például:

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.

Ez létrehozza a kezdőfájlokat egy alapszintű "Hello World" alkalmazáshoz.

Mielőtt elkezdené a módosításokat, futtassa az egyszerű Hello World alkalmazást. Az alkalmazás létrehozása után írja be dotnet run a parancssorba. Ez a parancs futtatja a NuGet-csomag visszaállítási folyamatát, létrehozza a végrehajtható alkalmazást, és futtatja a végrehajtható fájlt.

Az egyszerű Hello World alkalmazáskód teljes egészében a Program.cs fájlban található. Nyissa meg a fájlt a kedvenc szövegszerkesztőjével. Cserélje le a Program.cs kódját a következő kódra:

namespace TeleprompterConsole;

internal class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Hello World!");
    }
}

A fájl tetején található egy `namespace` utasítás. Más objektumorientált nyelvekhez hasonlóan a C# névtereket használ a típusok rendszerezéséhez. Ez a Hello World program sem különbözik. Láthatja, hogy a program a névtérben van a névvel TeleprompterConsole.

A fájl olvasása és visszhangja

Az első hozzáadandó funkció egy szövegfájl olvasása és az összes szöveg megjelenítése a konzolon. Először adjunk hozzá egy szövegfájlt. Másolja a sampleQuotes.txt fájlt a minta GitHub-adattárából a projektkönyvtárba. Ez lesz az alkalmazás szkriptje. Az oktatóanyag mintaalkalmazásának letöltésével kapcsolatos információkért tekintse meg a Minták és oktatóanyagok című témakör utasításait.

Ezután adja hozzá a következő metódust az Program osztályhoz (közvetlenül a Main metódus alatt):

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

Ez a metódus a C# metódus egy speciális típusa, az iterátor metódus. Az iterátor metódusok lazán kiértékelt sorozatokat adnak vissza. Ez azt jelenti, hogy a sorozat minden eleme létre lesz hozva, amint azt a sorozatot használó kód kéri. Az iterátor metódusok egy vagy több yield return utasítást tartalmazó metódusok. A metódus által ReadFrom visszaadott objektum tartalmazza a sorozat egyes elemeit létrehozó kódot. Ebben a példában a forrásfájl következő szövegsorának beolvasásáról és a karakterlánc visszaadásáról van szó. Minden alkalommal, amikor a hívó kód a következő elemet kéri le a sorozatból, a kód beolvassa a következő szövegsort a fájlból, és visszaadja azt. A fájl teljes olvasása után a sorozat azt jelzi, hogy nincs több elem.

Két C#-szintaxiselem lehet új. Az using ebben a metódusban található utasítás kezeli az erőforrás-törlést. Az utasításban inicializált változónak (readerebben a using példában) implementálnia kell az interfésztIDisposable. Ez az interfész egyetlen metódust határoz meg, Disposeamelyet az erőforrás felszabadításakor kell meghívni. A fordító akkor hozza létre ezt a hívást, amikor a végrehajtás eléri az using utasítás záró zárójelét. A fordító által létrehozott kód biztosítja, hogy az erőforrás még akkor is felszabaduljon, ha kivétel keletkezik a using utasítással definiált blokk kódjának végrehajtása közben.

A reader változó a var kulcsszó használatával van definiálva. var implicit módon beírt helyi változót határoz meg. Ez azt jelenti, hogy a változó típusát a változóhoz rendelt objektum fordítási ideje határozza meg. Itt ez a metódus visszatérési OpenText(String) értéke, amely egy StreamReader objektum.

Most töltsük ki a kódot, hogy beolvassuk a fájlt a Main metódusban:

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

Futtassa a programot a következő parancs használatával: dotnet run, és az összes sort látni fogja a konzolon.

Késések hozzáadása és kimenet formázása

A megjelenített adatok túl gyorsan jelennek meg a hangos olvasáshoz. Most hozzá kell adnia a kimenet késéseit. Az első lépésben az aszinkron feldolgozást lehetővé tevő alapvető kód egy részét fogja létrehozni. Ezek az első lépések azonban néhány antimintát követnek. A kód hozzáadásakor a megjegyzésekben jelennek meg az anti-minták, és a kód a későbbi lépésekben kerül frissítésre.

Ennek a szakasznak két lépése van. Először frissítenie kell az iterátor metódust, hogy a teljes sorok helyett egyetlen szavakat adjanak vissza. Ezeket a módosításokat már elvégeztük. Cserélje le az utasítást yield return line; a következő kódra:

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

Ezután meg kell változtatnia a fájl sorainak feldolgozását, és elhalasztást kell beiktatnia az egyes szavak leírása után. Cserélje le a Console.WriteLine(line)Main metódusban szereplő utasítást a következő blokkra:

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

Futtassa a mintát, és ellenőrizze a kimenetet. Most minden szót külön megjelenítünk, majd egy 200 ms késleltetés következik. A megjelenített kimenet azonban problémákat jelez, mert a forrásszövegfájl több sorból áll, amelyek több mint 80 karakterből állnak sortörés nélkül. Nehéz lehet olvasni, miközben az görgetés közben halad. Ezt könnyű kijavítani. Csak nyomon követheti az egyes sorok hosszát, és új vonalat hozhat létre, amikor a vonal hossza eléri a küszöbértéket. Deklaráljon egy helyi változót a words deklarálása után az ReadFrom metódusban, amely a sorhosszt tartalmazza:

var lineLength = 0;

Ezután adja hozzá a következő kódot az yield return word + " "; utasítás után (a záró zárójel előtt):

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

Futtassa a mintát, és az előre konfigurált ütemben felolvassa.

Aszinkron feladatok

Ebben az utolsó lépésben hozzáadja azt a kódot, amely aszinkron módon írja a kimenetet egy feladatba, miközben egy másik feladatot is futtat a felhasználótól érkező bemenet olvasásához, ha fel szeretné gyorsítani vagy lelassítani a szövegmegjelenítést, vagy teljesen le szeretné állítani a szövegmegjelenítést. Ez tartalmaz néhány lépést, és a végére minden szükséges frissítést megkap. Az első lépés egy aszinkron Task visszatérési módszer létrehozása, amely az eddig létrehozott kódot jelöli a fájl olvasásához és megjelenítéséhez.

Adja hozzá ezt a metódust az Program osztályhoz (ez a Main metódus törzséből származik):

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

Két módosítást fog látni. Először is, a metódus törzsében, ahelyett, hogy a Wait()-t szinkron módon várakoztatnánk egy feladat befejezésére, ez a verzió a await kulcsszót használja. Ehhez hozzá kell adnia a async módosítót a metódus aláírásához. Ez a metódus visszaad egy Task. Figyelje meg, hogy nincsenek olyan visszatérési utasítások, amelyek objektumot Task adnak vissza. Ehelyett az Task objektumot a fordító által generált kód hozza létre, amikor az await operátort használja. El lehet képzelni, hogy ez a metódus akkor tér vissza, amikor eléri a await. A visszaadott Task érték azt jelzi, hogy a munka nem fejeződött be. A metódus a várt tevékenység befejeződésekor folytatódik. Amikor befejeződött, a visszaadott Task érték azt jelzi, hogy befejeződött. A hívási kód figyelheti a visszaadott Task kódot, hogy megállapítsa, mikor fejeződött be.

Adjon hozzá egy await kulcsszót a ShowTeleprompter hívás elé:

await ShowTeleprompter();

Ehhez a metódus aláírását a Main következőre kell módosítania:

static async Task Main(string[] args)

További információ a módszerről azasync Main alapismeretek szakaszban.

Ezután meg kell írnia a második aszinkron metódust a konzolról való olvasáshoz, és figyelni kell a "<" (kisebb, mint), ">" (nagyobb, mint) és az "X" vagy "x" billentyűket. A feladat elvégzéséhez adjon hozzá egy metódust:

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

Ez létrehoz egy lambda kifejezést, amely egy Action olyan meghatalmazottat jelöl, aki beolvassa a konzol egyik kulcsát, és módosít egy helyi változót, amely azt a késést jelzi, amikor a felhasználó lenyomja a "<" (kisebb) vagy ">" (nagyobb) kulcsokat. A delegálási módszer akkor fejeződik be, amikor a felhasználó lenyomja az "X" vagy az "x" billentyűket, így a felhasználó bármikor leállíthatja a szövegmegjelenítést. Ez a módszer ReadKey() segítségével blokkol és vár, amíg a felhasználó lenyom egy billentyűt.

Itt az ideje, hogy létrehozzon egy osztályt, amely képes kezelni a megosztott adatokat a két feladat között. Ez az osztály két nyilvános tulajdonságot tartalmaz: a késleltetést és a fájl teljes olvasását jelző jelzőt Done :

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

Új fájl létrehozása; lehet bármilyen név, amely .cs végződik. Például TelePrompterConfig.cs. Illessze be a TelePrompterConfig osztálykódot, mentse és zárja be. Helyezze az osztályt a névtérbe az TeleprompterConsole ábrán látható módon. Vegye figyelembe, hogy az using static utasítás lehetővé teszi, hogy Min és Max metódusokra hivatkozzon az osztály- vagy névtérnevek használata nélkül. Egy using static utasítás importálja a metódusokat egy osztályból. Ez ellentétben áll a using anélküli staticutasítással, amely az összes osztályt egy névtérből importálja.

Ezután frissítenie kell a ShowTeleprompter és GetInput metódusokat, hogy az új config objektumot használják. A funkció befejezéséhez létre kell hoznia egy új async Task visszatérési módszert, amely mindkét tevékenységet elindítja (GetInput és ShowTeleprompter), és kezeli a két tevékenység közötti megosztott adatokat is. Hozzon létre egy RunTelePrompter-tevékenységet a tevékenységek elindításához és az első tevékenység befejeződésekor való kilépéshez:

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

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

Itt az egyik új módszer a WhenAny(Task[]) hívás. Ez létrehoz egy Task olyan elemet, amely az argumentumlistában szereplő bármely tevékenység befejeződésekor azonnal befejeződik.

Ezután frissítenie kell mind a ShowTeleprompter és GetInput metódusokat, hogy a config objektumot használják a késleltetéshez. A konfigurációs objektum paraméterként lesz átadva ezeknek a metódusoknak. A másolás/beillesztés használatával teljes mértékben lecserélheti a metódusokat az új kódra. Láthatja, hogy a kód attribútumokat és hívási metódusokat használ a konfigurációs objektumból:

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

Most frissítenie kell Main, hogy a RunTeleprompter-t hívja a ShowTeleprompter helyett.

await RunTeleprompter();

Következtetés

Ez az oktatóanyag bemutatta a C# nyelv és a konzolalkalmazások használatához kapcsolódó .NET Core-kódtárak számos funkcióját. Erre a tudásra támaszkodva többet is megtudhat a nyelvről és az itt bemutatott osztályokról. Megismerkedett a Fájl és konzol I/O alapjaival, a feladatalapú aszinkron programozás blokkolásával és letiltásával, a C# nyelvének és a C# programok rendszerezésének módjával, valamint a .NET CLI-vel.

További információ a fájl I/O-járól: Fájl és stream I/O. Az oktatóanyagban használt aszinkron programozási modellről további információt a feladatalapú aszinkron programozás és az aszinkron programozás című témakörben talál.