Semantik för lazy-utvärdering
Förstå uppskjuten körning av iteratorer.
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.
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
- Iterator-metoder med yield return
- yield break och tidig avslutning
- Semantik för lazy-utvärdering
- Egna enumerable-typer