0Pricing
C# Academy · Lezione

Semantica della valutazione lazy

Comprenda l'esecuzione differita degli iteratori.

Semantica della valutazione lazy è una lezione C# Academy gratuita su CoddyKit. Questa è la lezione 3 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento C# Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso C# Academy include 4 lezioni in totale.

Esecuzione differita

Gli iteratori e le query LINQ usano l'esecuzione differita: definire la query non esegue quasi nessuna operazione. Il lavoro viene svolto solo quando enumera effettivamente il risultato, elemento per elemento.

Nulla viene eseguito finché non si enumera

Chiamare un metodo iteratore non ne esegue il corpo. Il corpo inizia l'esecuzione solo quando un foreach (o un altro consumer) richiede il primo valore.

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

I valori vengono prodotti uno alla volta

Ogni iterazione esegue solo il codice necessario a produrre il valore successivo. Questo modello basato sulla richiesta mantiene basso il consumo di memoria anche per sequenze molto grandi.

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

Una nuova enumerazione esegue di nuovo

Una sequenza differita non dispone di un risultato memorizzato nella cache. Enumerarla due volte esegue l'iteratore due volte, aspetto importante se l'origine cambia o se l'operazione è costosa.

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

Leggere lo stato corrente

Poiché l'esecuzione è differita, un iteratore legge l'origine al momento dell'enumerazione, non al momento della definizione. Le modifiche apportate dopo la definizione della query sono visibili.

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

Forzare l'esecuzione immediata

Per acquisire un'istantanea dei risultati, materializzi la sequenza con ToList() o ToArray(). In questo modo l'iteratore viene eseguito una volta e i valori vengono memorizzati, rendendoli indipendenti dalle modifiche successive all'origine.

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

L'interruzione anticipata riduce il lavoro

Poiché i consumer richiedono i valori in modo lazy, un'operazione come la ricerca del primo elemento corrispondente può interrompersi in anticipo. Il produttore non calcola mai i valori che non vengono richiesti.

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

Eccezioni differite

Un'eccezione all'interno di un iteratore non viene generata quando si chiama il metodo, ma quando l'enumerazione raggiunge la riga che causa l'errore. Questo può sorprendere se si prevede una convalida anticipata.

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

Le pipeline lazy si combinano in modo efficiente

Collegare operatori lazy non li esegue ripetutamente. Ogni valore attraversa l'intera pipeline una sola volta, su richiesta, con un uso efficiente della memoria e delle risorse.

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

Quando materializzare

Materializzi la sequenza quando deve enumerarla più volte, quando l'origine potrebbe cambiare o quando deve acquisire i risultati prima di rilasciare una risorsa. In caso contrario, mantenga l'esecuzione lazy per risparmiare memoria e lavoro.

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

Provi personalmente

Osservi direttamente l'esecuzione differita: viene definita una query, l'origine viene modificata e le modifiche compaiono nei risultati solo al momento dell'enumerazione.

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

Verifica rapida

Ragioni sull'esecuzione differita.

Riepilogo

La valutazione lazy (differita) esegue una sequenza solo quando viene enumerata.

  • Definire una query non esegue alcun lavoro; lo fa il consumo della query.
  • Una nuova enumerazione esegue nuovamente l'iteratore.
  • Le origini vengono lette al momento dell'enumerazione, quindi le modifiche successive sono visibili.
  • Le eccezioni vengono generate durante l'enumerazione.
  • Usi ToList()/ToArray() per materializzare la sequenza quando necessario.

Domande Frequenti

La lezione «Semantica della valutazione lazy» è gratuita?

Sì — il testo completo di «Semantica della valutazione lazy» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso C# Academy, passa a CoddyKit PRO. Il corso C# Academy include 4 lezioni in totale.

Cosa imparerò in «Semantica della valutazione lazy»?

Comprenda l'esecuzione differita degli iteratori. Eserciti C# Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare C# Academy?

Non è richiesta alcuna esperienza precedente. C# Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 3 di 4.

Quanto tempo richiede la lezione «Semantica della valutazione lazy»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione C# Academy?

Sì. Ogni lezione C# Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. Metodi iteratori con yield return
  2. yield break e terminazione anticipata
  3. Semantica della valutazione lazy
  4. Enumerabili personalizzati
← Torna a C# Academy