Címke: C#

  • Tipikus interjú kérdés: yield

    Mennyi mindent használunk, de igazából lehet, hogy fogalmunk sincs róla, hogy mire is jó pontosan, és mikor is érdemes használni. A miértről nem is beszélve…

    Mit mesél erről a Gemini?

    A .NET-ben (C#) a yield kulcsszó pontosan ugyanazt a célt szolgálja, mint a Pythonban: IEnumerable<T> vagy IAsyncEnumerable<T> típusú adatfolyamokat hozhatsz vele létre úgy, hogy a kód lusta kiértékeléssel (lazy evaluation), elemenként adja vissza az eredményeket.

    C#-ban két formája létezik:

    • yield return <érték>: Visszaad egy elemet, majd felfüggeszti a metódus futását a következő elem kéréséig.
    • yield break: Azonnal leállítja az iterációt (megszakítja a generálást).

    Mire jó C#-ban?

    1. Memóriatakarékosság nagy adathalmazoknál

    Ha nem List<T>-et adsz vissza, nem kell a teljes gyűjteményt lefoglalni a RAM-ban.

    C#

    using System;
    using System.Collections.Generic;
    
    // PÉLDA 1: Hagyományos megközelítés (Memóriatakaréktalan)
    public List<int> GetNumbersList()
    {
        var list = new List<int>();
        for (int i = 0; i < 1_000_000; i++)
        {
            list.Add(i); // Mind az 1 milló elem belekerül a RAM-ba
        }
        return list;
    }
    
    // PÉLDA 2: yield használata (Memóriatakarékos)
    public IEnumerable<int> GetNumbersYield()
    {
        for (int i = 0; i < 1_000_000; i++)
        {
            yield return i; // Mindig csak az aktuális számot generálja le
        }
    }
    

    2. Szűrés és adatfeldolgozás yield break-kel

    A yield break használatával feltételhez kötheted az adatfolyam végét anélkül, hogy bonyolult feltételkezelést kellene írnod a ciklus köré:

    C#

    public IEnumerable<string> GetActiveUsers(List<string> users)
    {
        foreach (var user in users)
        {
            if (user == "ADMIN_STOP")
            {
                yield break; // Azonnal leállítja az elemek kiadását
            }
    
            if (!string.IsNullOrEmpty(user))
            {
                yield return user;
            }
        }
    }
    

    3. Óriási fájlok feldolgozása soronként

    Nem kell a gigabájtos fájlokat egyszerre a memóriába tölteni (File.ReadAllLines helyett):

    C#

    using System.IO;
    using System.Collections.Generic;
    
    public IEnumerable<string> ReadLargeFile(string filePath)
    {
        using (StreamReader reader = new StreamReader(filePath))
        {
            string? line;
            while ((line = reader.ReadLine()) != null)
            {
                yield return line; // Mindig csak egyetlen sort tart a memóriában
            }
        }
    }
    
    // Használat:
    foreach (string line in ReadLargeFile("huge_log.txt"))
    {
        if (line.Contains("ERROR"))
        {
            Console.WriteLine(line);
        }
    }
    

    4. Aszinkron adatfolyamok (C# 8.0+: IAsyncEnumerable<T>)

    Modern .NET-ben hálózati kéréseknél vagy adatbázis-lekérdezéseknél aszinkron módon is használhatod:

    C#

    public async IAsyncEnumerable<int> FetchDataAsync()
    {
        for (int i = 1; i <= 5; i++)
        {
            await Task.Delay(1000); // Pl. hálózati várakozás szimulálása
            yield return i;
        }
    }
    
    // Használat:
    await foreach (var item in FetchDataAsync())
    {
        Console.WriteLine(item); // Másodpercenként jelenik meg egy elem
    }
    

    Hagyományos visszatérés vs. yield C#-ban

    SzempontList<T> / Tömbyield return (IEnumerable<T>)
    Kiértékelés idejeAzonnali (A metódus hívásakor lefut a teljes ciklus)Lusta / Halasztott (Csak akkor fut, amikor a foreach kéri a következő elemet)
    MemóriahasználatMagas ($O(N)$ méretű lista a RAM-ban)Minimális ($O(1)$ állandó memóriagény)
    Sebesség (első elem)Lassú (meg kell várni a teljes lista felépítését)Azonnali (az első elem kérésére rögtön visszatér)

    Gondoltam, megkérdezem ugyanezt Typescript esetén.

    Igen, pontosan ugyanígy működik TypeScriptben és JavaScriptben is.

    A TypeScript a JavaScript generátor függvényeit (Generator Functions) használja, amelyeket az function* (csillaggal jelölt) szintaxisról lehet felismerni.

    A működési elv teljesen megegyezik a Python és a C# esetében látottakkal: a yield felfüggeszti a függvény futását, megőrzi a belső állapotot, és lusta kiértékeléssel (lazy evaluation) adja vissza az elemeket egy Generator objektumon keresztül.

    Alapvető példa TypeScriptben

    A generátor függvény visszatérési típusa Generator<T, TNext TReturn,>, ahol a T a yield-elt értékek típusa.

    TypeScript

    // A csillag (function*) jelzi, hogy ez egy generátor függvény
    function* szamGeneraltor(): Generator<number> {
      yield 1;
      yield 2;
      yield 3;
    }
    
    // Használat for...of ciklussal:
    for (const szam of szamGeneraltor()) {
      console.log(szam); // Kiírja egymás után: 1, 2, 3
    }
    

    Mire jó TypeScriptben?

    1. Memóriatakarékos és végtelen sorozatok

    Nem kell előre tömbbe gyűjteni az elemeket:

    TypeScript

    function* vegtelenAzonositoGeneral(): Generator<string> {
      let id = 1;
      while (true) {
        yield `ID_${id++}`;
      }
    }
    
    const idGen = vegtelenAzonositoGeneral();
    
    console.log(idGen.next().value); // "ID_1"
    console.log(idGen.next().value); // "ID_2"
    console.log(idGen.next().value); // "ID_3"
    

    2. Adatfolyamok delegálása (yield*)

    A TypeScriptben létezik a yield* utasítás is, amellyel átadhatod a vezérlést egy másik generátornak vagy tömbnek (bejárható objektumnak):

    TypeScript

    function* elsoResz(): Generator<number> {
      yield 1;
      yield 2;
    }
    
    function* teljesSorozat(): Generator<number> {
      yield* elsoResz(); // Delegálja az elemeket az elsoResz-ből
      yield* [3, 4, 5];  // Tömb elemeit is ki tudja adni egymás után
    }
    
    console.log([...teljesSorozat()]); // [1, 2, 3, 4, 5]
    

    3. Aszinkron generátorok (yield + async)

    Ahogy C#-ban az IAsyncEnumerable, TypeScriptben és JS-ben is létezik az AsyncGenerator (async function*). Ez hálózati adatok, lapozott API-k vagy adatfolyamok (stream) feldolgozásánál nagyon hasznos:

    TypeScript

    async function* adatokLetolteseAPIrol(): AsyncGenerator<string> {
      const oldalak = [1, 2, 3];
      
      for (const oldal of oldalak) {
        // Szimulált hálózati kérés (API hívás)
        const valasz = await fetch(`https://api.example.com/data?page=${oldal}`);
        const adat = await valasz.text();
        
        yield adat; // Mindig csak az aktuális oldal adatait adja vissza
      }
    }
    
    // Használat for await...of ciklussal:
    async function feldolgozas() {
      for await (const oldalAdat of adatokLetolteseAPIrol()) {
        console.log("Megérkezett egy oldal:", oldalAdat);
      }
    }
    

    Összehasonlítás

    NyelvFüggvény jelöléseKulcsszóBejárási felület
    C#IEnumerable<T> Get()yield returnforeach / LINQ
    Pythondef get():yieldfor item in gen
    TypeScriptfunction* get(): Generator<T>yieldfor...of / next()
  • Automatizáló framework-ök a szakmám során

    Eddigi pályafutásom során jó pár framework-höz volt szerencsém különféle programnyelvek mellett, és hát azt kell mondanom, hogy ideje ezeket összegyűjtenem, mert kezdem egy részét elfelejteni.

    • Selenium WebDriver (#C, JAVA)
    • HP UFT (Visual Basic Script)
    • Katalon Studio (Kotlin)
    • Cypress (JavaScript)
    • Playwright (TypeScript)

    Selenium + C# + NUnit

    Kezdjünk is bele. Szóval még az automatizáló pályám elején volt lehetőségem a munkám mellett gyakorolni, és az épp aktuális Selenium volt a választott C#-pal. Ez akkoriban azt jelentette, hogy ahogy ma egy demó kód kinéz, kb úgy írtam én is meg Visual Studioban, majd pedig NUnitban a saját gépemről futottak a tesztek. Attól függetlenül, hogy messze volt egy professziónális megoldástól, szépen fogta a regressziós hibákat.

    Valahogy így kell elképzelni, ahogy a tesztek futottak.

    HP UFT + Visual Basic Script + HP ALM

    Ez volt az első hivatalos automatizálós projektem, ami előtt még kaptam HP képzést is, bár ennél a munka és a szükséges tudás összeszedése internetről sokkal hasznosabbnak bizonyult. Ahogy nézem, a HP dolgai igencsak megváltoztak, más kezeli őket.

    HP ALM kapcsán a Gurunál találtam egy értékelhető leírást, és a jól emlékszem, én ennek az oldalnak használtam fel a tudásanyagát annak idején. Viszont jól látszik, hogy elszaladt az idő felette. Talán az egyik cimborám kérdezte pár éve, hogy mi a véleményem egy ilyen projektről, és már akkor azt mondtam, hogy kerülje el, mert nincs jövője.

    Annak idején ez az ökoszisztéma igencsak jól működött, bár ára is volt (cég fizette). Az ALM végezte a központosítást, az UFT pedig az automata tesztek fejlesztésért volt felelős.

    Így nézett ki az UFT.

    Az ALM pedig így.

    Szerintem ez a kettő együtt akkoriban zseniális párost alkotott.

    Katalon Studio

    Kísérletezgettem vele annak idején. Még tetszett is, de valahogy körülményesnek ítéltem meg a használatát. Ennél egy IDEs kódszerkesztővel jobban és minőségibben lehet/lehetett dolgozni. Az pozitív volt, hogy próbálta egy IDE-n belül kezelni a dolgokat, mint a HP UFT, viszont egy idő után kezdett átláthatatlanná válni számomra. Azóta egyébként valószínűleg ez is sokat fejlődött, bár nekem nem volt időm követni a változásokat.

    Valami ilyesmire emlékszem.

    Cypress + JavaScript

    Na ezt imádtam. Könnyedén és gyorsan lehetett benne dolgozni, bár csak 1 évet töltöttem vele. Egy negatívuma azért van/volt, hogy az async/await dolgokat maga kezeli. Ez az esetek túlnyomó többségében nagyon jól működik, de amikor valamiért ez nem jó, az fájdalmas tud lenni.

    Ha jól rémlik, én is VS Code-dal használtam, illetve GitLab-ban futottak a tesztek.

    Ha jól sejtem VS Codeban.

    Playwright + TypeScript

    Abszolút kedvenc. 3 évet töltöttem vele, és imádtam minden percet. Annyira könnyed és légies, és gyorsan lehet vele haladni annak köszönhetően, hogy zseniálisan van felépítve benne szinte minden, hogy csak ajánlani tudom.

    Több különböző nyelvet is támogat, bár nekem a TypeScript volt használatban. Illetve nagyon jól dokumentált. Nem volt szükség sok Google keresésre, mivel a leírás hozzá megválaszolt mindent.

    Ez pedig ilyen volt.