C# Academy · Lektion

Semantik för lazy-utvärdering

Förstå uppskjuten körning av iteratorer.

Lektion 3 av 413 steg

Semantik för lazy-utvärdering är en gratis lektion i C# Academy på CoddyKit. Detta är lektion 3 av 4. Ni kan läsa hela lektionen gratis nedan och sedan öva praktiskt i webbläsaren med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt. Den ingår i lärvägen för C# Academy, och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i C# Academy innehåller totalt 4 lektioner.

Fördröjd exekvering

Iteratorer och LINQ-frågor använder fördröjd exekvering: att definiera frågan gör nästan ingenting. Arbetet utförs först när ni faktiskt räknar upp resultatet, element för element.

Inget körs förrän ni räknar upp sekvensen

Att anropa en iteratormetod kör inte dess bröd. Brödets körning börjar först när en foreach (eller en annan konsument) hämtar det första värdet.

using System;
using System.Collections.Generic;

public class Program
{
    static IEnumerable<int> Numbers()
    {
        Console.WriteLine("-- iterator body started --");
        yield return 1;
        yield return 2;
    }

    public static void Main()
    {
        var seq = Numbers();
        Console.WriteLine("query created, body not run yet");
        foreach (var n in seq) Console.WriteLine(n);
    }
}

Värden produceras ett i taget

Varje iteration kör precis så mycket av iteratorn som behövs för att producera nästa värde. Den här pull-baserade modellen håller minnesanvändningen låg även för mycket stora sekvenser.

using System;
using System.Collections.Generic;

public class Program
{
    static IEnumerable<int> Trace()
    {
        for (int i = 1; i <= 3; i++)
        {
            Console.WriteLine("producing " + i);
            yield return i;
        }
    }

    public static void Main()
    {
        foreach (var n in Trace())
            Console.WriteLine("consumed " + n);
    }
}

Ny uppräkning körs igen

En fördröjd sekvens har inget cachat resultat. Om ni räknar upp den två gånger körs iteratorn två gånger, vilket är viktigt om källan ändras eller arbetet är kostsamt.

using System;
using System.Collections.Generic;

public class Program
{
    static int _calls = 0;
    static IEnumerable<int> Counter()
    {
        _calls++;
        yield return _calls;
    }

    public static void Main()
    {
        var seq = Counter();
        foreach (var n in seq) Console.WriteLine(n);
        foreach (var n in seq) Console.WriteLine(n);
        Console.WriteLine("total runs: " + _calls);
    }
}

Avläsa aktuellt tillstånd

Eftersom körningen är fördröjd läser en iterator sin källa när uppräkningen sker, inte när den definieras. Ändringar som görs efter att frågan har definierats blir synliga.

using System;
using System.Collections.Generic;

public class Program
{
    public static void Main()
    {
        var list = new List<int> { 1, 2 };
        IEnumerable<int> doubled = Double(list);
        list.Add(3); // added before enumeration
        foreach (var n in doubled) Console.WriteLine(n);
    }

    static IEnumerable<int> Double(IEnumerable<int> nums)
    {
        foreach (var n in nums) yield return n * 2;
    }
}

Tvinga omedelbar körning

Skapa en ögonblicksbild av resultaten genom att materialisera sekvensen med ToList() eller ToArray(). Då körs iteratorn en gång och värdena lagras, så att de kopplas bort från senare ändringar i källan.

using System;
using System.Collections.Generic;
using System.Linq;

public class Program
{
    public static void Main()
    {
        var list = new List<int> { 1, 2 };
        List<int> snapshot = Double(list).ToList(); // executed now
        list.Add(99);
        Console.WriteLine(string.Join(",", snapshot));
    }

    static IEnumerable<int> Double(IEnumerable<int> nums)
    {
        foreach (var n in nums) yield return n * 2;
    }
}

Tidig avbrytning sparar arbete

Eftersom konsumenter hämtar värden på ett lazy sätt kan en operation som söker efter den första träffen avsluta tidigt. Producenten beräknar aldrig värden som ingen efterfrågar.

using System;
using System.Collections.Generic;
using System.Linq;

public class Program
{
    static IEnumerable<int> Numbers()
    {
        for (int i = 1; ; i++)
        {
            Console.WriteLine("checking " + i);
            yield return i;
        }
    }

    public static void Main()
    {
        int firstBig = Numbers().First(n => n > 3);
        Console.WriteLine("found " + firstBig);
    }
}

Fördröjda undantag

Ett undantag inuti en iterator kastas inte när metoden anropas, utan när uppräkningen når raden som orsakar felet. Det kan överraska er om ni förväntar er att valideringen ska ske direkt.

using System;
using System.Collections.Generic;

public class Program
{
    static IEnumerable<int> Risky(int divisor)
    {
        yield return 10 / divisor; // throws only during enumeration
    }

    public static void Main()
    {
        var seq = Risky(0);
        Console.WriteLine("no error yet");
        try { foreach (var n in seq) Console.WriteLine(n); }
        catch (DivideByZeroException) { Console.WriteLine("caught during enumeration"); }
    }
}

Lata pipelines kombineras effektivt

Att kedja lata operatorer kör dem inte upprepade gånger. Varje värde passerar genom hela pipelinen en gång, vid behov, vilket är effektivt och minnessnålt.

using System;
using System.Collections.Generic;
using System.Linq;

public class Program
{
    public static void Main()
    {
        var result = Enumerable.Range(1, 1000000)
            .Where(n => n % 2 == 0)
            .Select(n => n * n)
            .Take(3);
        foreach (var n in result) Console.WriteLine(n);
    }
}

När sekvensen ska materialiseras

Materialisera när ni kommer att räkna upp flera gånger, när källan kan ändras eller när ni måste spara resultaten innan en resurs frigörs. Annars bör ni behålla en lazy sekvens för att spara minne och arbete.

using System;
using System.Collections.Generic;
using System.Linq;

public class Program
{
    public static void Main()
    {
        var evens = Enumerable.Range(1, 10).Where(n => n % 2 == 0).ToList();
        // Safe to enumerate many times now
        Console.WriteLine("count: " + evens.Count);
        Console.WriteLine("sum: " + evens.Sum());
    }
}

Prova själv

Observera fördröjd exekvering direkt: en fråga definieras, källan ändras och först när uppräkningen sker syns ändringarna i resultaten.

using System;
using System.Collections.Generic;
using System.Linq;

public class Program
{
    public static void Main()
    {
        var source = new List<int> { 1, 2, 3 };
        var query = source.Where(n => n > 1); // not executed yet
        source.Add(10);
        source.Remove(2);
        // Executes now, reflecting all changes
        Console.WriteLine(string.Join(", ", query));
    }
}

Snabbtest

Resonera kring fördröjd exekvering.

Sammanfattning

Lazy, eller fördröjd, utvärdering kör en sekvens först när den räknas upp.

  • Att definiera en fråga utför inget arbete; det gör däremot konsumtionen.
  • En ny uppräkning kör iteratorn igen.
  • Källor läses när uppräkningen sker, så senare ändringar blir synliga.
  • Undantag uppstår under uppräkningen.
  • Använd ToList()/ToArray() för att materialisera vid behov.
Gratis att börja

Lär dig C# med en AI-lärare – gratis

Skriv och kör riktig kod i webbläsaren, få omedelbar hjälp av en AI-lärare dygnet runt och fortsätt där du slutade – på webben eller i appen.

Kurser
93
Lektioner
346

Vanliga frågor

Är lektionen ”Semantik för lazy-utvärdering” gratis?

Ja – hela texten till ”Semantik för lazy-utvärdering” kan läsas gratis här på webben. Om Ni vill öva interaktivt med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt och låsa upp resten av kursen i C# Academy, kan Ni uppgradera till CoddyKit PRO. Kursen i C# Academy innehåller totalt 4 lektioner.

Vad lär jag mig i ”Semantik för lazy-utvärdering”?

Förstå uppskjuten körning av iteratorer. Ni övar på C# Academy med praktisk kod som körs direkt i webbläsaren, medan en AI-handledare som är tillgänglig dygnet runt svarar på Era frågor under lektionen.

Behöver jag någon erfarenhet för att börja lära mig C# Academy?

Du behöver inga förkunskaper. Utbildningen i C# Academy på CoddyKit är upplagd för allt från nybörjare till avancerade elever, så att du kan börja här eller från början och gå fram i din egen takt. Detta är lektion 3 av 4.

Hur lång tid tar lektionen ”Semantik för lazy-utvärdering”?

De flesta CoddyKit-lektioner tar cirka 5–10 minuter. Varje lektion är kort och interaktiv, så att du gör stadiga framsteg och kan fortsätta precis där du slutade – på webben eller i appen.

Kan jag skriva och köra kod i den här C# Academy-lektionen?

Ja. Varje C# Academy-lektion innehåller en inbyggd kodredigerare, så att du kan skriva och köra riktig kod direkt i webbläsaren och få omedelbar AI-feedback – utan lokal installation.

Alla lektioner i den här kursen

  1. Iterator-metoder med yield return
  2. yield break och tidig avslutning
  3. Semantik för lazy-utvärdering
  4. Egna enumerable-typer
← Tillbaka till C# Academy