Select e SelectMany em C#: qual escolher para velocidade?

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Select e SelectMany em C#: qual a diferença e como isso afeta a velocidade?



Se você já escreveu consultas LINQ em C#, certamente já se deparou com dois métodos semelhantes, mas muito diferentes: Select e SelectMany. À primeira vista, ambos "selecionam algo" de uma coleção. Mas o diabo, como sempre, está nos detalhes. A escolha errada entre eles pode transformar seu código rápido em uma bagunça lenta, enquanto a escolha certa pode torná-lo elegante e extremamente rápido.



Neste artigo, não vamos apenas analisar a teoria. Vamos realizar um teste de desempenho real, observar o funcionamento interno de ambos os métodos e dar a você um algoritmo claro: quando usar Select e quando usar SelectMany. Pronto? Vamos lá!



Breve introdução: o que são Select e SelectMany?



Para começar, o básico. Imagine que você tem uma lista de alunos (List<Student>), e cada aluno tem uma lista de notas (List<int>).



Select é uma projeção "um para um". Para cada elemento da coleção original, você obtém exatamente um elemento na saída. Se você tem 10 alunos, Select retornará uma coleção de 10 elementos.



  • Exemplo: Obter os nomes de todos os alunos.
  • Assinatura: IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector)


SelectMany é uma projeção "um para muitos" seguida de "achatamento" (flatten). Cada elemento da coleção original pode gerar vários elementos na saída, e todos eles são combinados em uma única lista plana.



  • Exemplo: Obter todas as notas de todos os alunos (uma única lista plana de números).
  • Assinatura: IEnumerable<TResult> SelectMany<TSource, TCollection, TResult>(this IEnumerable<TSource> source, Func<TSource, IEnumerable<TCollection>> collectionSelector, Func<TSource, TCollection, TResult> resultSelector)


Parece que a diferença é óbvia. Mas onde está o problema? O problema está em como esses métodos funcionam internamente e como os iniciantes frequentemente os confundem.



Principal diferença: formato dos dados de saída



Vamos fixar isso com um exemplo de código. Aqui está nossa classe modelo:



public class Student{    public string Name { get; set; }    public List<int> Grades { get; set; } = new List<int>();}

// Criando os dadosvar students = new List<Student>{ new Student { Name = "Ana", Grades = new List<int>{ 5, 4, 5 } }, new Student { Name = "Bruno", Grades = new List<int>{ 3, 4 } }, new Student { Name = "Carla", Grades = new List<int>{ 5, 5, 5, 4 } }};


Exemplo 1: Usando Select (incorreto)



// Queremos obter todas as notas. Mas Select retornará uma coleção de coleções!var allGradesNested = students.Select(s => s.Grades);// Tipo: IEnumerable<List<int>> — três listas internas!

foreach (var gradeList in allGradesNested){ foreach (var grade in gradeList) { Console.Write(grade + " "); // É necessário usar um loop aninhado }}// Saída: 5 4 5 3 4 5 5 5 4


Isso funciona, mas é inconveniente. Você obtém uma estrutura aninhada que precisa ser desdobrada adicionalmente.



Exemplo 2: Usando SelectMany (correto)



// SelectMany faz todo o trabalho para nós: pega as coleções internas e as "achata"var allGradesFlat = students.SelectMany(s => s.Grades);// Tipo: IEnumerable<int> — uma única lista plana de números!

foreach (var grade in allGradesFlat){ Console.Write(grade + " "); // Sem loops aninhados!}// Saída: 5 4 5 3 4 5 5 5 4


Sente a diferença

Blogs

Book Recommendations