Enumeráveis personalizados
Implemente IEnumerable com iteradores.
Enumeráveis personalizados é uma aula grátis de C# Academy no CoddyKit. Esta é a aula 4 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de C# Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de C# Academy inclui 4 aulas no total.
Criando seu próprio enumerável
Ao implementar IEnumerable<T> em uma classe, você pode usá-la em um foreach. Combinado com métodos iteradores, isso permite criar coleções e intervalos personalizados com pouquíssimo código.
Implementando GetEnumerator com yield
A maneira mais simples de implementar IEnumerable<T> é escrever GetEnumerator como um iterador usando yield return. O compilador cria o enumerador para você.
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);
}
}Por que existem dois métodos GetEnumerator?
IEnumerable<T> herda IEnumerable, que não é genérico, portanto você implementa ambos. O método explícito não genérico simplesmente delega à versão genérica.
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);
}
}Encapsulando lógica personalizada
Um enumerável personalizado pode ocultar regras complexas de geração por trás de um tipo simples. Aqui, uma classe produz uma sequência Fibonacci com um tamanho escolhido.
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);
}
}Encapsulando uma coleção interna
Um enumerável personalizado geralmente encapsula um armazenamento interno, expondo a iteração e controlando como os itens são adicionados ou filtrados.
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);
}
}Enumeráveis personalizados funcionam com LINQ
Qualquer tipo que implemente IEnumerable<T> passa imediatamente a ter todos os recursos do LINQ. Sua coleção personalizada pode ser filtrada, projetada e agregada sem trabalho adicional.
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());
}
}Cada foreach obtém um enumerador novo
Como GetEnumerator é chamado sempre que você itera, um enumerável personalizado pode ser enumerado repetidamente, com cada passagem começando do zero.
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();
}
}Enumerável personalizado genérico
Torne o enumerável genérico para que ele possa armazenar qualquer tipo de elemento. Aqui, um ring simples expõe seus itens em ordem, começando em um deslocamento escolhido.
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);
}
}Enumeráveis personalizados preguiçosos
Como GetEnumerator é um iterador, o seu enumerável personalizado é preguiçoso por padrão: os itens só são produzidos à medida que o consumidor os solicita.
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 criar um enumerável personalizado
Use um enumerável personalizado quando a ordem de iteração ou a lógica de geração for uma parte essencial de um tipo, como em uma travessia de árvore, uma origem de dados paginada ou uma sequência matemática. Caso contrário, um método iterador comum é suficiente.
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);
}
}Experimente por si mesmo
Crie um enumerável personalizado que produza uma sequência Geometric e depois use LINQ nele. Seu tipo se integra diretamente a todo o ecossistema.
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ção rápida
Relembre o contrato de IEnumerable<T>.
Recapitulação
Um enumerável personalizado implementa IEnumerable<T> para funcionar com foreach e LINQ.
- Implemente
GetEnumeratorcomo um iterador comyield return. - Também forneça o
GetEnumeratorexplícito não genérico. - Cada iteração obtém um enumerador novo.
- A iteração é preguiçosa por padrão.
Perguntas Frequentes
A aula “Enumeráveis personalizados” é grátis?
Sim — o texto completo de “Enumeráveis personalizados” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de C# Academy, atualize para CoddyKit PRO. O curso de C# Academy inclui 4 aulas no total.
O que vou aprender em “Enumeráveis personalizados”?
Implemente IEnumerable com iteradores. Você pratica C# Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.
Preciso ter experiência prévia para começar C# Academy?
Nenhuma experiência prévia é necessária. C# Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 4 de 4.
Quanto tempo leva a aula “Enumeráveis personalizados”?
A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.
Posso escrever e executar código nesta aula de C# Academy?
Sim. Cada aula de C# Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.
Todas as aulas deste curso
- Métodos iteradores com yield return
- yield break e encerramento antecipado
- Semântica da avaliação tardia
- Enumeráveis personalizados