How to Join a List of Strings into CSV in C#: Under the Hood Analysis

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Introduction: Why Joining Strings Is Not Just "Concatenation"

\n\n

Imagine: you are writing a web service in C# that receives a list of tags from a user and needs to send them to the database as a single comma-separated string. Or you are generating a CSV report for export. Or you are logging a set of identifiers. Sound familiar? The question "Creating a comma separated list from IList or IEnumerable" on Stack Overflow has gathered thousands of views — and for good reason.

\n\n

It seems simple enough, right? Take a list of strings, loop through it, add commas. But in practice, this task hides several pitfalls: from trailing commas to catastrophic performance degradation on large data. In this article, we will not just show ready-made solutions but look under the hood of each one. You will learn how string.Join, StringBuilder, and even LINQ Aggregate work, and learn to choose the right tool for your task.

\n\n

Let's go!

\n\n

The Problem: A Simple Request, Non-Obvious Complexities

\n\n

Suppose we have a collection of strings:

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

We want to get: "Apple,Banana,Cherry,Date". The first thing that comes to a beginner's mind is to write a 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

This code works, but it does so terribly. Why? Because strings in C# are immutable. Each += operation creates a new string in memory, copying the contents of the previous one. With 4 elements, this is unnoticeable, but with 10,000 strings, you get quadratic complexity O(n²) and massive memory consumption. Under the hood — thousands of allocations and garbage collections.

\n\n

Clearly, a smarter approach is needed. And here come the main protagonists.

\n\n

Method 1: string.Join — The King of Performance

\n\n

The most correct and performant way is to use the static method string.Join:

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

One line of code — and it's done. Let's look under the hood of string.Join (in .NET Core / .NET 5+).

\n\n

What happens inside?

\n
    \n
  • Length calculation: The method first iterates through the entire collection (if it's an IEnumerable<T>) to find the total number of characters. If the collection is an array or IList, it immediately gets the length via Count.
  • \n
  • Buffer allocation: One large character array (char[]) of the required size is allocated: the sum of all string lengths + (number of strings - 1) * separator length.
  • \n
  • Copying: All strings and separators are copied into this buffer without a single extra allocation.
  • \n
  • String creation: One final string is created from the buffer.
  • \n
\n\n

Result: one memory allocation (for the final string) + allocation of a temporary buffer. No intermediate strings. Complexity — strictly O(n).

\n\n

For IList<string>, the method works even faster because it can immediately get Count and the length of each element via an indexer, without calling GetEnumerator.

\n\n

Method 2: StringBuilder — An Old Friend

\n\n

Before the optimized string.Join appeared (or if you are stuck on an older version of .NET Framework), the standard solution was 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