So far we've worked with variables that store a single piece of data: a title, a page count, a boolean. But in practice, almost any program needs to handle several pieces of data of the same type at once — for example, all of BiblioTech's book titles — and also needs to manipulate text in a more advanced way than a simple Console.WriteLine. In this lesson you'll learn to work with arrays (single- and multi-dimensional), to iterate over them with foreach/for, and to make the most of the string class: concatenation, interpolation, and its most common methods. We'll wrap up with StringBuilder, the right tool when you need to concatenate text repeatedly inside a loop.

Contents

  1. Single-dimensional arrays
  2. Multi-dimensional arrays
  3. Fixed-size arrays versus List<T>
  4. Iterating over an array with foreach and for
  5. The string class: immutability, concatenation, and interpolation
  6. Common string methods
  7. StringBuilder for efficient concatenation

Single-dimensional arrays

An array is a collection of elements of the same type, stored contiguously and accessed through a numeric index starting at 0. It's declared by specifying the elements' type followed by square brackets []:

string[] bookTitles = new string[3];
bookTitles[0] = "One Hundred Years of Solitude";
bookTitles[1] = "Hopscotch";
bookTitles[2] = "Ficciones";

Console.WriteLine(bookTitles[0]); // One Hundred Years of Solitude

It can also be declared and initialized in a single step, specifying the values directly:

string[] bookTitles = { "One Hundred Years of Solitude", "Hopscotch", "Ficciones" };

// Equivalent, more explicit form:
string[] bookTitlesB = new string[] { "One Hundred Years of Solitude", "Hopscotch", "Ficciones" };

Fixed size and the Length property

A fundamental characteristic of arrays in C# is that their size is fixed when they're created and cannot change afterward. To find out how many elements it contains, you use the Length property:

string[] bookTitles = { "One Hundred Years of Solitude", "Hopscotch", "Ficciones" };
Console.WriteLine(bookTitles.Length); // 3

Out-of-range access

Trying to access an index that doesn't exist (for example, bookTitles[5] in an array with 3 elements) doesn't cause a compile error, but it does cause a runtime error (an IndexOutOfRangeException, which we'll study in Module 2 when we cover exception handling). That's why you must always make sure the index used is between 0 and Length - 1.

string[] bookTitles = { "One Hundred Years of Solitude", "Hopscotch", "Ficciones" };
// Console.WriteLine(bookTitles[3]); // Runtime error: index out of range
Console.WriteLine(bookTitles[bookTitles.Length - 1]); // Ficciones (the last element)

Multi-dimensional arrays

Besides single-dimensional arrays (a simple list of elements), C# allows multi-dimensional arrays, useful for representing data organized as a table or grid. The most common case is the two-dimensional array (rows and columns):

// An array of 2 rows (shelves) x 3 columns (slots per shelf)
string[,] shelf = new string[2, 3];

shelf[0, 0] = "One Hundred Years of Solitude";
shelf[0, 1] = "Hopscotch";
shelf[0, 2] = "Ficciones";
shelf[1, 0] = "The Aleph";
shelf[1, 1] = "Pedro Páramo";
shelf[1, 2] = "The House of the Spirits";

Console.WriteLine(shelf[1, 2]); // The House of the Spirits

It can also be initialized directly with its values:

string[,] shelf =
{
    { "One Hundred Years of Solitude", "Hopscotch", "Ficciones" },
    { "The Aleph", "Pedro Páramo", "The House of the Spirits" }
};

To find the number of rows and columns in a two-dimensional array, you use the GetLength method, specifying the dimension (0 for rows, 1 for columns):

Console.WriteLine(shelf.GetLength(0)); // 2 (rows / shelves)
Console.WriteLine(shelf.GetLength(1)); // 3 (columns / slots per shelf)

In this course, multi-dimensional arrays will appear only occasionally; most examples will use single-dimensional arrays, which are by far the most common in everyday use.

Fixed-size arrays versus List<T>

As we've seen, an array has a fixed size: once created with, say, 3 elements, you can't add a fourth book title without creating a completely new array. In many real scenarios (such as gradually adding books to the BiblioTech catalog as they're registered), this limitation is inconvenient.

For those cases, .NET offers List<T>, a collection whose size can grow and shrink dynamically, with methods like Add (to add an element) or Remove (to remove it). We only mention it in passing here: List<T> and the rest of .NET's collections are studied in depth in Module 4 ("Collections"), together with LINQ, the powerful query tool we'll use to explore BiblioTech's full catalog. For now, keep this in mind:

Array (string[]) List<T> (for example, List<string>)
Size Fixed when created Dynamic (grows and shrinks)
When to use it The number of elements is known ahead of time and doesn't change The number of elements can vary at runtime
Studied in detail in This module Module 4

Iterating over an array with foreach and for

To process every element of an array, one by one, there are two main loops. Although loops are studied in depth in Module 2 ("Control Flow"), here we'll see their basic use applied to arrays, since it's such a common combination that it's worth knowing right away.

foreach: iterating over every element

foreach iterates over every element of a collection, one after another, without needing to manually manage an index:

string[] bookTitles = { "One Hundred Years of Solitude", "Hopscotch", "Ficciones" };

foreach (string title in bookTitles)
{
    Console.WriteLine(title);
}

This code prints each title in the array, one per line. foreach is the clearest and recommended option when you simply need to read or process each element, without needing its position (index).

for: iterating with explicit control over the index

When you do need the index (for example, to number the books, or to modify the array itself while iterating), you use the for loop, which gives you explicit control over a counter:

string[] bookTitles = { "One Hundred Years of Solitude", "Hopscotch", "Ficciones" };

for (int i = 0; i < bookTitles.Length; i++)
{
    Console.WriteLine($"{i + 1}. {bookTitles[i]}");
}

This example prints each title preceded by its position in the list, starting at 1 (adding 1 to the index, which starts at 0):

1. One Hundred Years of Solitude
2. Hopscotch
3. Ficciones
foreach for
Access to the index No (only each element's value) Yes, through the counter (i)
Modifying the array while iterating Not recommended Possible, with care
Readability Simpler and more direct More flexible but somewhat more verbose

The string class: immutability, concatenation, and interpolation

Immutability

A string in C# is immutable: once created, its content can't be changed. Any operation that "seems" to change a string (such as concatenating it with other text) actually creates a new string in memory, leaving the original untouched:

string originalTitle = "Hopscotch";
string modifiedTitle = originalTitle + " (special edition)";

Console.WriteLine(originalTitle);  // Hopscotch (unchanged)
Console.WriteLine(modifiedTitle);  // Hopscotch (special edition)

This property has important performance implications when concatenating many strings inside a loop, as we'll see later in the StringBuilder section.

Concatenation

Concatenating means joining two or more strings into one. The most basic way is with the + operator:

string title = "One Hundred Years of Solitude";
string author = "Gabriel García Márquez";
string description = title + " - " + author;

Console.WriteLine(description); // One Hundred Years of Solitude - Gabriel García Márquez

String interpolation ($"")

Interpolation is a much more readable way to build strings that combine fixed text with variable values. You put a $ symbol before the quotes, and variables (or even expressions) are inserted directly inside braces { }:

string title = "One Hundred Years of Solitude";
int pages = 471;
bool available = true;

string summary = $"The book '{title}' has {pages} pages and is available: {available}";
Console.WriteLine(summary);

Result:

The book 'One Hundred Years of Solitude' has 471 pages and is available: True

Inside the braces { } of an interpolated string you can even write small expressions, not just plain variables:

int pagesRead = 120;
int totalPages = 471;

Console.WriteLine($"Reading progress: {pagesRead} of {totalPages} pages ({pagesRead * 100 / totalPages}%)");
Technique Example Readability
Concatenation with + "Book: " + title + ", pages: " + pages Gets hard to read with many variables
Interpolation $"" $"Book: {title}, pages: {pages}" Clear even with several variables

In this course we'll prefer interpolation whenever we combine text with variables, for the sake of clarity.

Common string methods

The string class includes numerous built-in methods for the most common text manipulation tasks. Here are the most widely used:

Method What it does Example
Split Splits a string into an array of substrings, based on a separator "a,b,c".Split(',')["a", "b", "c"]
Trim Removes whitespace from the start and end " Hopscotch ".Trim()"Hopscotch"
Contains Checks whether a string contains another "Hopscotch".Contains("psc")true
Substring Extracts a portion of the string, given a starting position (and optionally a length) "Hopscotch".Substring(0, 3)"Hop"
ToUpper / ToLower Converts the whole string to uppercase or lowercase "Hopscotch".ToUpper()"HOPSCOTCH"

Let's see them applied to a realistic BiblioTech example: processing a line of text with several pieces of book data separated by commas, similar to how it might come from an import file (something we'll cover in depth in Module 5, "File Input/Output").

string importedLine = "  Hopscotch, Julio Cortázar, 635  ";

string cleanLine = importedLine.Trim();      // removes leading/trailing whitespace
string[] fields = cleanLine.Split(',');      // splits on commas: ["  Hopscotch", " Julio Cortázar", " 635  "]

string title = fields[0].Trim();     // "Hopscotch"
string author = fields[1].Trim();    // "Julio Cortázar"
string pagesText = fields[2].Trim(); // "635"

Console.WriteLine($"Title: {title}");
Console.WriteLine($"Author: {author}");
Console.WriteLine($"Pages (text): {pagesText}");

// Searching for text within a title
bool containsScot = title.Contains("scot");
Console.WriteLine($"The title contains 'scot': {containsScot}");

// Extracting the first 3 letters of the title
string titlePrefix = title.Substring(0, 3);
Console.WriteLine($"Title prefix: {titlePrefix}");

// Normalizing to uppercase for case-insensitive comparisons
string titleUppercase = title.ToUpper();
Console.WriteLine(titleUppercase); // HOPSCOTCH

Notice that none of these methods modifies the original string (remember: strings are immutable); instead, each one returns a new string with the result. That's why we always assign the result to a variable (or use it directly), instead of expecting the original variable to change on its own.

Comparing strings while ignoring case

A very common use case when searching a catalog (like BiblioTech's) is comparing text without caring about uppercase or lowercase. To do this, you can convert both strings to the same case before comparing, or use the Equals method with a specific option:

string searchTerm = "hopscotch";
string catalogTitle = "Hopscotch";

bool matches = catalogTitle.Equals(searchTerm, StringComparison.OrdinalIgnoreCase);
Console.WriteLine(matches); // True

StringBuilder for efficient concatenation

Since every string is immutable, repeatedly concatenating strings inside a loop (for example, to build a report with every title in the catalog) creates, on every iteration, a whole new string in memory, discarding the previous one. For a handful of elements this isn't a problem at all, but if the loop runs many, many times (thousands of books, for example), it can noticeably hurt performance.

For these cases, .NET offers the StringBuilder class, designed specifically to build strings efficiently through successive modifications, without creating a whole new string at every step. It lives in the System.Text namespace.

using System.Text;

string[] bookTitles = { "One Hundred Years of Solitude", "Hopscotch", "Ficciones", "The Aleph" };

StringBuilder report = new StringBuilder();
report.Append("BiblioTech catalog:");
report.AppendLine(); // line break

foreach (string title in bookTitles)
{
    report.AppendLine($"- {title}");
}

string finalReport = report.ToString();
Console.WriteLine(finalReport);

Result:

BiblioTech catalog:
- One Hundred Years of Solitude
- Hopscotch
- Ficciones
- The Aleph
Situation Recommendation
Concatenating a few strings, outside a loop + or $"" interpolation
Concatenating inside a loop that may run many times StringBuilder

We won't go any deeper into StringBuilder in this introductory module; it's a tool that will naturally keep coming up later in the course, especially when we work with large book catalogs and report generation.

Common Mistakes and Tips

  • Accessing an out-of-range index: remember that an array's indices run from 0 to Length - 1. Accessing array[Length] is a very common mistake and causes a runtime exception.
  • Expecting an array to change size: arrays have a fixed size. If you need to add or remove elements dynamically, you'll need List<T> (Module 4), not an array. Creating a bigger array and copying the data over is possible, but cumbersome and uncommon in practice.
  • Forgetting that strings are immutable: writing title.ToUpper(); without assigning the result to any variable doesn't modify title; you need to write title = title.ToUpper(); (or use the result directly wherever it's needed).
  • Using + concatenation inside large loops: for a handful of elements it's fine, but if the loop processes a huge number of elements, StringBuilder is preferable.
  • Using Split without cleaning up whitespace: when splitting text with Split, each fragment may carry extra whitespace; it's a good idea to apply Trim() to each one, as we did in the data-import example.
  • Tip: when working with text that combines variables, always prefer interpolation ($"...") over + concatenation: the resulting code is much more readable and less prone to errors.

Exercises

  1. Declare a string[] array with at least 5 BiblioTech book titles. Iterate over it with foreach and display each title on its own line. Then iterate over it with a for loop, showing each title numbered, starting at 1 (for example: 1. One Hundred Years of Solitude).

  2. Given the array of titles from the previous exercise, write code that iterates over the array and shows only the titles that contain the letter "a" (using the Contains method, converting the title to lowercase with ToLower() before comparing, so the search is case-insensitive).

  3. Using StringBuilder, build a text report with all the titles from the array in exercise 1, with the format:

    === BiblioTech Catalog ===
    1) <title 1>
    2) <title 2>
    ...
    

    (Hint: you can combine a for loop with StringBuilder.AppendLine and string interpolation to build each numbered line).

Solutions

string[] bookTitles = { "One Hundred Years of Solitude", "Hopscotch", "Ficciones", "The Aleph", "Pedro Páramo" };

// Iterating with foreach
foreach (string title in bookTitles)
{
    Console.WriteLine(title);
}

Console.WriteLine("---");

// Iterating with for, numbered from 1
for (int i = 0; i < bookTitles.Length; i++)
{
    Console.WriteLine($"{i + 1}. {bookTitles[i]}");
}
string[] bookTitles = { "One Hundred Years of Solitude", "Hopscotch", "Ficciones", "The Aleph", "Pedro Páramo" };

foreach (string title in bookTitles)
{
    if (title.ToLower().Contains("a"))
    {
        Console.WriteLine(title);
    }
}

Every title in the example contains the letter "a" somewhere (uppercase or lowercase), so the result would show all five; the exercise is meant to practice combining ToLower() and Contains(), rather than to obtain a smaller subset with this particular data. You can try it with other titles that don't contain the letter to check that filtering actually works.

using System.Text;

string[] bookTitles = { "One Hundred Years of Solitude", "Hopscotch", "Ficciones", "The Aleph", "Pedro Páramo" };

StringBuilder report = new StringBuilder();
report.AppendLine("=== BiblioTech Catalog ===");

for (int i = 0; i < bookTitles.Length; i++)
{
    report.AppendLine($"{i + 1}) {bookTitles[i]}");
}

Console.WriteLine(report.ToString());

Expected result:

=== BiblioTech Catalog ===
1) One Hundred Years of Solitude
2) Hopscotch
3) Ficciones
4) The Aleph
5) Pedro Páramo

Conclusion

In this lesson you learned to work with single- and multi-dimensional arrays, to iterate over them with foreach and for, and you saw why List<T> (which we'll study in Module 4) solves the fixed-size limitation of arrays. You also went deeper into the string class: its immutability, concatenation, $"" interpolation, its most common methods (Split, Trim, Contains, Substring, ToUpper/ToLower), and the use of StringBuilder to concatenate text efficiently inside loops.

This closes Module 1: Introduction to C#. You now know how to install and configure your environment, write and run console programs, correctly apply the language's syntax, declare variables of different types, and work with arrays and strings — all of it applied, step by step, to the BiblioTech project. In Module 2: Control Structures you'll learn to make decisions in your code with conditional statements, to repeat actions with loops, to organize multiple cases with switch, and to handle errors robustly with exceptions: essential pieces for BiblioTech to start behaving like a real application, capable of reacting to different situations and data.

© Copyright 2026. All rights reserved