0Pricing
C# Academy · Lektion

Semantik der verzögerten Auswertung

Verstehen Sie die verzögerte Ausführung von Iteratoren.

Semantik der verzögerten Auswertung ist eine kostenlose C# Academy-Lektion auf CoddyKit. Dies ist Lektion 3 von 4. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des C# Academy-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der C# Academy-Kurs umfasst insgesamt 4 Lektionen.

Verzögerte Ausführung

Iteratoren und LINQ-Abfragen verwenden die verzögerte Ausführung: Das Definieren der Abfrage bewirkt fast nichts. Die Arbeit beginnt erst, wenn Sie das Ergebnis tatsächlich Element für Element enumerieren.

Nichts wird ausgeführt, bevor Sie enumerieren

Der Aufruf einer Iteratormethode führt deren Rumpf nicht aus. Die Ausführung des Rumpfs beginnt erst, wenn eine foreach-Schleife (oder ein anderer Consumer) den ersten Wert abruft.

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

Werte werden einzeln erzeugt

Bei jeder Iteration wird genau so viel des Iterators ausgeführt, dass der nächste Wert erzeugt wird. Dieses Pull-basierte Modell hält den Speicherverbrauch auch bei sehr großen Sequenzen niedrig.

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

Erneutes Enumerieren führt erneut aus

Eine verzögerte Sequenz hat kein zwischengespeichertes Ergebnis. Wenn Sie sie zweimal enumerieren, wird der Iterator zweimal ausgeführt. Das ist wichtig, wenn sich die Quelle ändert oder die Verarbeitung aufwendig ist.

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

Den aktuellen Zustand erfassen

Da die Ausführung verzögert erfolgt, liest ein Iterator seine Quelle zum Zeitpunkt des Enumerierens und nicht zum Zeitpunkt der Definition. Änderungen, die nach dem Definieren der Abfrage vorgenommen werden, sind sichtbar.

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

Sofortige Ausführung erzwingen

Um Ergebnisse als Momentaufnahme zu speichern, materialisieren Sie die Sequenz mit ToList() oder ToArray(). Dadurch wird der Iterator einmal ausgeführt und die Werte werden gespeichert, sodass spätere Änderungen an der Quelle keinen Einfluss mehr haben.

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

Kurzschlussauswertung spart Arbeit

Da Consumer Werte verzögert abrufen, kann eine Operation wie die Suche nach dem ersten Treffer vorzeitig beendet werden. Der Produzent berechnet niemals Werte, die nicht angefordert werden.

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

Verzögert auftretende Ausnahmen

Eine Ausnahme innerhalb eines Iterators wird nicht beim Aufruf der Methode ausgelöst, sondern erst, wenn das Enumerieren die fehlerhafte Zeile erreicht. Das kann überraschend sein, wenn Sie eine Validierung bereits beim Aufruf erwarten.

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

Lazy-Pipelines lassen sich effizient kombinieren

Das Verketten verzögerter Operatoren führt diese nicht wiederholt aus. Jeder Wert durchläuft die gesamte Pipeline genau einmal und nur bei Bedarf. Das ist effizient und speicherschonend.

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

Wann Sie materialisieren sollten

Materialisieren Sie, wenn Sie mehrfach enumerieren werden, wenn sich die Quelle ändern kann oder wenn Sie Ergebnisse vor der Freigabe einer Ressource erfassen müssen. Bleiben Sie andernfalls verzögert, um Speicher und Arbeit zu sparen.

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

Probieren Sie es selbst aus

Beobachten Sie die verzögerte Ausführung direkt: Eine Abfrage wird definiert, die Quelle wird verändert, und erst beim Enumerieren werden die Änderungen in den Ergebnissen sichtbar.

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

Kurztest

Denken Sie über verzögerte Ausführung nach.

Zusammenfassung

Die verzögerte (Lazy-)Auswertung führt eine Sequenz erst beim Enumerieren aus.

  • Das Definieren einer Abfrage bewirkt nichts; erst das Verarbeiten führt sie aus.
  • Erneutes Enumerieren führt den Iterator erneut aus.
  • Quellen werden zum Zeitpunkt des Enumerierens gelesen, daher sind spätere Änderungen sichtbar.
  • Ausnahmen treten beim Enumerieren auf.
  • Verwenden Sie ToList()/ToArray(), um bei Bedarf zu materialisieren.

Häufig gestellte Fragen

Ist die Lektion „Semantik der verzögerten Auswertung“ kostenlos?

Ja — der vollständige Text von „Semantik der verzögerten Auswertung“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des C# Academy-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der C# Academy-Kurs umfasst insgesamt 4 Lektionen.

Was lerne ich in „Semantik der verzögerten Auswertung“?

Verstehen Sie die verzögerte Ausführung von Iteratoren. Du übst C# Academy mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.

Brauche ich Erfahrung, um C# Academy zu starten?

Keine Vorkenntnisse erforderlich. C# Academy auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 3 von 4.

Wie lange dauert die Lektion „Semantik der verzögerten Auswertung“?

Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.

Kann ich in dieser C# Academy-Lektion Code schreiben und ausführen?

Ja. Jede C# Academy-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.

Alle Lektionen in diesem Kurs

  1. Iterator-Methoden mit yield return
  2. yield break und vorzeitige Beendigung
  3. Semantik der verzögerten Auswertung
  4. Benutzerdefinierte Enumerables
← Zurück zu C# Academy