C# Academy · Lezione

Enumerabili personalizzati

Implementi IEnumerable con gli iteratori.

Lezione 4 di 413 passaggi

Enumerabili personalizzati è una lezione C# Academy gratuita su CoddyKit. Questa è la lezione 4 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.

Creare il proprio enumerabile

Implementando IEnumerable<T> in una classe, la rende utilizzabile in un foreach. In combinazione con i metodi iteratore, questo consente di creare raccolte e intervalli personalizzati con pochissimo codice.

Implementare GetEnumerator con yield

Il modo più semplice per implementare IEnumerable<T> consiste nello scrivere GetEnumerator come iteratore usando yield return. Il compilatore crea l'enumeratore al posto Suo.

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

public class IntRange : IEnumerable<int>
{
    private readonly int _start, _count;
    public IntRange(int start, int count) { _start = start; _count = count; }

    public IEnumerator<int> GetEnumerator()
    {
        for (int i = 0; i < _count; i++)
            yield return _start + i;
    }
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

public class Program
{
    public static void Main()
    {
        foreach (var n in new IntRange(5, 4))
            Console.WriteLine(n);
    }
}

Perché due metodi GetEnumerator?

IEnumerable<T> eredita l'interfaccia non generica IEnumerable, quindi è necessario implementare entrambi i metodi. Quello non generico esplicito si limita a delegare alla versione generica.

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

public class Letters : IEnumerable<char>
{
    public IEnumerator<char> GetEnumerator()
    {
        yield return 'x';
        yield return 'y';
        yield return 'z';
    }
    // Required because IEnumerable<T> extends IEnumerable
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

public class Program
{
    public static void Main()
    {
        foreach (var c in new Letters())
            Console.WriteLine(c);
    }
}

Incapsulare la logica personalizzata

Un enumerabile personalizzato può nascondere regole di generazione complesse dietro un tipo semplice da usare. In questo caso, una classe restituisce una sequenza di Fibonacci di lunghezza scelta.

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

public class Fibonacci : IEnumerable<int>
{
    private readonly int _count;
    public Fibonacci(int count) { _count = count; }

    public IEnumerator<int> GetEnumerator()
    {
        int a = 0, b = 1;
        for (int i = 0; i < _count; i++)
        {
            yield return a;
            int next = a + b; a = b; b = next;
        }
    }
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

public class Program
{
    public static void Main()
    {
        foreach (var n in new Fibonacci(8))
            Console.WriteLine(n);
    }
}

Racchiudere una raccolta interna

Un enumerabile personalizzato spesso racchiude una memoria interna, esponendo l'iterazione e controllando il modo in cui gli elementi vengono aggiunti o filtrati.

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

public class EvenBag : IEnumerable<int>
{
    private readonly List<int> _items = new List<int>();
    public void Add(int n) { if (n % 2 == 0) _items.Add(n); }

    public IEnumerator<int> GetEnumerator() => _items.GetEnumerator();
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

public class Program
{
    public static void Main()
    {
        var bag = new EvenBag();
        bag.Add(1); bag.Add(2); bag.Add(3); bag.Add(4);
        foreach (var n in bag) Console.WriteLine(n);
    }
}

Gli enumerabili personalizzati funzionano con LINQ

Qualsiasi tipo che implementa IEnumerable<T> ottiene immediatamente tutte le funzionalità di LINQ. La raccolta personalizzata può essere filtrata, proiettata e aggregata senza lavoro aggiuntivo.

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

public class IntRange : IEnumerable<int>
{
    private readonly int _start, _count;
    public IntRange(int start, int count) { _start = start; _count = count; }
    public IEnumerator<int> GetEnumerator()
    {
        for (int i = 0; i < _count; i++) yield return _start + i;
    }
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

public class Program
{
    public static void Main()
    {
        var range = new IntRange(1, 10);
        Console.WriteLine(range.Where(n => n % 3 == 0).Sum());
    }
}

Ogni foreach ottiene un enumeratore nuovo

Poiché GetEnumerator viene chiamato ogni volta che si esegue un'iterazione, un enumerabile personalizzato può essere enumerato più volte, iniziando da capo a ogni passaggio.

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

public class IntRange : IEnumerable<int>
{
    private readonly int _count;
    public IntRange(int count) { _count = count; }
    public IEnumerator<int> GetEnumerator()
    {
        for (int i = 1; i <= _count; i++) yield return i;
    }
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

public class Program
{
    public static void Main()
    {
        var r = new IntRange(3);
        foreach (var n in r) Console.Write(n);
        Console.WriteLine();
        foreach (var n in r) Console.Write(n); // fresh again
        Console.WriteLine();
    }
}

Enumerabile personalizzato generico

Renda l'enumerabile generico per poter contenere elementi di qualsiasi tipo. In questo caso, un semplice anello espone i propri elementi in ordine a partire da un offset scelto.

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

public class Ring<T> : IEnumerable<T>
{
    private readonly T[] _items;
    private readonly int _start;
    public Ring(T[] items, int start) { _items = items; _start = start; }

    public IEnumerator<T> GetEnumerator()
    {
        for (int i = 0; i < _items.Length; i++)
            yield return _items[(_start + i) % _items.Length];
    }
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

public class Program
{
    public static void Main()
    {
        var ring = new Ring<string>(new[] { "a", "b", "c", "d" }, 2);
        foreach (var s in ring) Console.WriteLine(s);
    }
}

Enumerabili personalizzati lazy

Poiché GetEnumerator è a sua volta un iteratore, l'enumerabile personalizzato è lazy per impostazione predefinita: gli elementi vengono prodotti solo quando il consumer li richiede.

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

public class Squares : IEnumerable<int>
{
    private readonly int _count;
    public Squares(int count) { _count = count; }
    public IEnumerator<int> GetEnumerator()
    {
        for (int i = 1; i <= _count; i++)
        {
            Console.WriteLine("computing " + i);
            yield return i * i;
        }
    }
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

public class Program
{
    public static void Main()
    {
        foreach (var n in new Squares(3))
            Console.WriteLine("got " + n);
    }
}

Quando creare un enumerabile personalizzato

Scelga un enumerabile personalizzato quando l'ordine di iterazione o la logica di generazione costituisce una parte fondamentale di un tipo, ad esempio per attraversare un albero, accedere a un'origine di dati paginata o rappresentare una sequenza matematica. In caso contrario, è sufficiente un semplice metodo iteratore.

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

public class Countdown : IEnumerable<int>
{
    private readonly int _from;
    public Countdown(int from) { _from = from; }
    public IEnumerator<int> GetEnumerator()
    {
        for (int i = _from; i >= 0; i--) yield return i;
    }
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

public class Program
{
    public static void Main()
    {
        foreach (var n in new Countdown(5)) Console.WriteLine(n);
    }
}

Provi personalmente

Crei un enumerabile personalizzato che produca una sequenza geometrica, quindi usi LINQ su di esso. Il tipo si integra direttamente nell'intero ecosistema.

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

public class Geometric : IEnumerable<int>
{
    private readonly int _start, _ratio, _count;
    public Geometric(int start, int ratio, int count) { _start = start; _ratio = ratio; _count = count; }
    public IEnumerator<int> GetEnumerator()
    {
        int value = _start;
        for (int i = 0; i < _count; i++) { yield return value; value *= _ratio; }
    }
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

public class Program
{
    public static void Main()
    {
        var seq = new Geometric(1, 2, 6);
        Console.WriteLine(string.Join(", ", seq));
        Console.WriteLine("sum: " + seq.Sum());
    }
}

Verifica rapida

Ricordi il contratto di IEnumerable.

Riepilogo

Un enumerabile personalizzato implementa IEnumerable<T> per funzionare con foreach e LINQ.

  • Implementi GetEnumerator come iteratore con yield return.
  • Fornisca anche il metodo non generico esplicito GetEnumerator.
  • Ogni iterazione ottiene un enumeratore nuovo.
  • L'iterazione è lazy per impostazione predefinita.
Gratis per iniziare

Impara C# con un tutor IA — gratis

Scrivi ed esegui vero codice nel tuo browser, ricevi aiuto istantaneo da un tutor IA disponibile 24/7, e riprendi da dove hai lasciato sul web o nell'app.

Corsi
93
Lezioni
346

Domande Frequenti

La lezione «Enumerabili personalizzati» è gratuita?

Sì — il testo completo di «Enumerabili personalizzati» è 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 «Enumerabili personalizzati»?

Implementi IEnumerable con gli 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 4 di 4.

Quanto tempo richiede la lezione «Enumerabili personalizzati»?

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