Como juntar uma lista de strings em CSV em C#: análise interna

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

Introdução: Por que juntar strings não é apenas "somar"

\n\n

Imagine: você está escrevendo um serviço web em C# que recebe uma lista de tags do usuário e precisa enviá-las ao banco de dados como uma única string separada por vírgulas. Ou você está gerando um relatório CSV para exportação. Ou registrando um conjunto de identificadores. Situação familiar? A pergunta "Creating a comma separated list from IList or IEnumerable" no Stack Overflow acumulou milhares de visualizações — e não é à toa.

\n\n

Parece simples, não? Pegue uma lista de strings, percorra com um loop, adicione vírgulas. Mas na prática, essa tarefa esconde várias armadilhas: desde vírgulas extras no final até uma queda catastrófica de desempenho em grandes volumes de dados. Neste artigo, não vamos apenas mostrar soluções prontas, mas vamos olhar por baixo dos panos de cada uma delas. Você aprenderá como funcionam string.Join, StringBuilder e até LINQ Aggregate, e saberá escolher a ferramenta certa para sua tarefa.

\n\n

Vamos nessa!

\n\n

O Problema: Uma pergunta simples, dificuldades não óbvias

\n\n

Suponha que temos uma coleção de strings:

\n\n
IEnumerable<string> fruits = new List<string> { "Apple", "Banana", "Cherry", "Date" };
\n\n

Queremos obter: "Apple,Banana,Cherry,Date". A primeira ideia de um iniciante é escrever um loop:

\n\n
string result = "";\nbool first = true;\nforeach (var fruit in fruits)\n{\n    if (!first)\n        result += ",";\n    result += fruit;\n    first = false;\n}
\n\n

Esse código funciona, mas o faz de forma péssima. Por quê? Porque strings em C# são imutáveis. Cada operação += cria uma nova string na memória, copiando o conteúdo da anterior. Com 4 elementos é imperceptível, mas com 10.000 strings você terá complexidade quadrática O(n²) e um consumo gigantesco de memória. Internamente, são milhares de alocações e coletas de lixo.

\n\n

Obviamente, precisamos de uma abordagem mais inteligente. E é aqui que entram os protagonistas.

\n\n

Método 1: string.Join — o rei do desempenho

\n\n

A maneira mais correta e eficiente é usar o método estático string.Join:

\n\n
string csv = string.Join(",", fruits);\nConsole.WriteLine(csv); // Apple,Banana,Cherry,Date
\n\n

Uma linha de código e pronto. Vamos olhar por baixo dos panos do string.Join (no .NET Core / .NET 5+).

\n\n

O que acontece internamente?

\n
    \n
  • Cálculo do comprimento: O método primeiro percorre toda a coleção (se for IEnumerable<T>) para saber o número total de caracteres. Se a coleção for um array ou IList, ele obtém o comprimento diretamente via Count.
  • \n
  • Alocação do buffer: Um grande array de caracteres (char[]) é alocado com o tamanho necessário: soma dos comprimentos de todas as strings + (quantidade de strings - 1) * comprimento do separador.
  • \n
  • Cópia: Todas as strings e separadores são copiados para esse buffer sem uma única alocação extra.
  • \n
  • Criação da string: A partir do buffer, é criada uma única string final.
  • \n
\n\n

Resultado: uma alocação de memória (para a string final) + alocação do buffer temporário. Nenhuma string intermediária. Complexidade estritamente O(n).

\n\n

Para IList<string>, o método é ainda mais rápido, pois pode obter imediatamente o Count e o comprimento de cada elemento através do indexador, sem chamar GetEnumerator.

\n\n

Método 2: StringBuilder — o velho amigo

\n\n

Antes do string.Join otimizado (ou se você estiver preso a uma versão antiga do .NET Framework), a solução padrão era o StringBuilder:

\n\n
var sb = new StringBuilder();\nbool first = true;\nforeach (var fruit in fruits)\n{\n    if (!first)\n        sb.Append(',');\n    sb                

Blogs

Book Recommendations