Skip to main content

Arrays and Strings: Marks, Names, Admission Numbers – C# | Lesson 33

Published / updated

Introduction​

Class 10 has six Mathematics marks from Unit Test 1. With what you know so far, you would need six variables, and a class of 50 would need 50.

An array holds all of them under one name, in numbered positions.

Most school data is also text: names, admission numbers, and remarks. C# strings come with dozens of ready-made methods for cleaning, searching, and formatting text.

By the end of this lesson, you will be able to:

  • Create arrays, read items by position, and loop over them.
  • Use index-from-end (^1) and ranges (1..4).
  • Format reports with interpolation, number formats, and column widths.
  • Clean, search, split, and compare strings.
  • Explain why strings never change, and when to use StringBuilder.

Why this matters​

A report card, a class list, and an export file are all "the same work for many items" plus "text formatted just right". Arrays and strings are the tools for both.

Strings also hide a classic performance trap. In this lesson, building an export of 10,000 students the obvious way took over 600 milliseconds, and the right way took 2.

On a server exporting for many schools, that difference matters.

The concept​

Example: an array​

Example
decimal[] marks = { 92m, 85m, 67m, 95m, 58m, 78m };
CodeResultMeaning
marks.Length6How many items
marks[0]92The first item; positions start at 0
marks[5]78The last item, at position Length - 1
marks[^1]78Index from the end: ^1 is the last item
names[1..4]Diya, Kabir, MeeraA range: from position 1 up to, but not including, 4

An array has a fixed size. Once created with six places, it always has six. Module 8 shows List<T>, which can grow.

Think of a row of numbered lockers in the corridor. Each locker holds one thing, and you find it by its number. Locker numbering starts at 0, which takes a week to get used to.

Example: strings never change​

A string is immutable: once created, it can never change. Methods like Trim() and ToUpper() do not change the original. They return a new string:

Example
string cleaned = guardianName.Trim();

If you call guardianName.Trim(); without saving the result, nothing happens. This is the most common string mistake.

Example: string methods you will use daily​

MethodExampleResult
Trim()" rajesh sharma ".Trim()"rajesh sharma"
ToUpper() / ToLower()"rajesh".ToUpper()"RAJESH"
Length"rajesh sharma".Length13
StartsWith / Contains"rajesh".StartsWith("raj")true
Split"ADM-2017-0003".Split('-')["ADM", "2017", "0003"]
string.Joinstring.Join(", ", names)"Aarav, Diya, ..."
[..3]"Malgudi Days"[..3]"Mal"

Example: special characters​

Some characters cannot be typed directly inside quotes. A backslash starts an escape sequence:

You writeYou get
"\n"A new line, used at the end of each CSV line in this lesson
"\""A double quote inside the text
"\\"One backslash
@"C:\Exports\students.csv"A verbatim string: backslashes are kept as typed, handy for Windows paths

Example: interpolation and formats​

$"..." puts values into text. After a colon or comma inside the braces, you control the format:

Inside the bracesMeaningExample result
{average:F2}Two decimal places79.17
{serial:D5}At least 5 digits, padded with zeros00042
{name,-8}8 characters wide, aligned leftAarav
{mark,5}5 characters wide, aligned right 92
{date:yyyy-MM-dd}A date format2026-07-31

Apply it to the SMS​

The program works with Class 10's Mathematics marks for Unit Test 1, exactly as stored in SchoolDb, then with names, admission numbers, and an export file.

Show src/SchoolApp.Console/Program.cs
src/SchoolApp.Console/Program.cs
// Lesson 33: arrays and strings, using Class 10's Mathematics marks (Unit Test 1) from SchoolDb.
using System.Diagnostics;
using System.Text;

// 1. Two arrays: the student at position i has the mark at position i.
string[] names = { "Aarav", "Diya", "Kabir", "Meera", "Rohan", "Sana" };
decimal[] marks = { 92m, 85m, 67m, 95m, 58m, 78m };

Console.WriteLine($"Students: {names.Length}");
Console.WriteLine($"First: {names[0]} {marks[0]} Last: {names[^1]} {marks[^1]}");
Console.WriteLine($"Positions 1 to 3: {string.Join(", ", names[1..4])}");

// 2. Loop over an array: total, average, and topper.
decimal total = 0;
int topperIndex = 0;

for (int i = 0; i < marks.Length; i++)
{
total += marks[i];
if (marks[i] > marks[topperIndex])
{
topperIndex = i;
}
}

decimal average = total / marks.Length;
Console.WriteLine($"Total {total}, average {average:F2}, topper {names[topperIndex]} ({marks[topperIndex]})");

// 3. A report with aligned columns: {value,-8} pads to the right, {value,5} to the left.
int position = 0;
foreach (string name in names)
{
Console.WriteLine($"{name,-8}{marks[position],5}");
position++;
}

// 4. Strings never change: methods return a new string.
string guardianName = " rajesh sharma ";
string cleaned = guardianName.Trim();
Console.WriteLine($"[{guardianName}] -> [{cleaned}] -> [{cleaned.ToUpper()}]");
Console.WriteLine($"Starts with 'raj': {cleaned.StartsWith("raj")}; length {cleaned.Length}");

// 5. Build and check admission numbers.
int admissionYear = 2026;
int serial = 42;
string admissionNumber = $"ADM-{admissionYear}-{serial:D5}";
Console.WriteLine($"New admission number: {admissionNumber}");

string typed = "adm-2017-0003";
string[] parts = typed.Split('-');
bool looksValid = parts.Length == 3
&& parts[0].Equals("ADM", StringComparison.OrdinalIgnoreCase)
&& int.TryParse(parts[1], out _) // _ = "I only need to know if it is a number"
&& int.TryParse(parts[2], out _);
Console.WriteLine($"'{typed}' has {parts.Length} parts; valid format: {looksValid}");
Console.WriteLine($"Same as ADM-2017-0003 (ignore case): {string.Equals(typed, "ADM-2017-0003", StringComparison.OrdinalIgnoreCase)}");

// 6. Building a 10,000-line CSV: += copies the whole text every time; StringBuilder does not.
Stopwatch timer = Stopwatch.StartNew();
string csv = "";
for (int i = 1; i <= 10000; i++)
{
csv += $"ADM-2026-{i:D5},Student {i}\n";
}
long plusMs = timer.ElapsedMilliseconds;

timer.Restart();
StringBuilder builder = new StringBuilder();
for (int i = 1; i <= 10000; i++)
{
builder.Append($"ADM-2026-{i:D5},Student {i}\n");
}
string csvFast = builder.ToString();
long builderMs = timer.ElapsedMilliseconds;

Console.WriteLine($"+= : {csv.Length} characters in {plusMs} ms");
Console.WriteLine($"StringBuilder: {csvFast.Length} characters in {builderMs} ms; same text: {csv == csvFast}");
Output
Students: 6
First: Aarav 92 Last: Sana 78
Positions 1 to 3: Diya, Kabir, Meera
Total 475, average 79.17, topper Meera (95)
Aarav 92
Diya 85
Kabir 67
Meera 95
Rohan 58
Sana 78
[ rajesh sharma ] -> [rajesh sharma] -> [RAJESH SHARMA]
Starts with 'raj': True; length 13
New admission number: ADM-2026-00042
'adm-2017-0003' has 3 parts; valid format: True
Same as ADM-2017-0003 (ignore case): True
+= : 278894 characters in 618 ms
StringBuilder: 278894 characters in 2 ms; same text: True

Example: total, average, and topper​

The for loop visits positions 0 to 5. It adds each mark to total, and remembers the position of the highest mark seen so far in topperIndex.

The result matches the database: Meera's 95 is the highest, and the average is 79.17. In Module 8, LINQ does this in one line, such as marks.Max(). Knowing the loop first helps you understand what LINQ does for you.

Example: two arrays side by side​

names[2] and marks[2] belong to the same student, Kabir. Two arrays kept in step like this are called parallel arrays.

They work for small examples, but they are fragile: sort one array and forget the other, and every mark belongs to the wrong student. In Module 7, one Student object holds a name and a mark together, which fixes this for good.

Example: building and checking admission numbers​

The SMS format is ADM- plus the year plus a number. $"ADM-{admissionYear}-{serial:D5}" builds ADM-2026-00042, with the zeros added by D5.

To check a typed admission number, Split('-') breaks it into parts. The check needs three parts, an ADM prefix, and numbers in the other two. out _ is a discard: it means "I only need to know whether it is a number, not the number itself".

Example: comparing text, ignoring case​

A parent types adm-2017-0003 in lowercase. With plain ==, it does not equal ADM-2017-0003, because == compares exact characters.

string.Equals(a, b, StringComparison.OrdinalIgnoreCase) ignores upper and lower case. Use it for codes, emails, and user names. Never "fix" this by calling ToUpper() on both sides inside a loop; that creates two new strings for every comparison.

Example: StringBuilder for building big text​

Because strings never change, csv += line creates a new, longer string each time, copying everything built so far. For 10,000 lines, that is 10,000 ever-growing copies.

StringBuilder keeps one growing buffer and adds to its end. Three runs on a laptop:

MethodRun 1Run 2Run 3Same text
+=618 ms676 ms672 msYes
StringBuilder2 ms2 ms2 msYes

Rule of thumb: joining a few strings with + or interpolation is fine. Building text in a loop needs a StringBuilder.

Common mistakes​

  1. Reading past the end of an array. marks[6] on a six-item array stops the program with IndexOutOfRangeException: Index was outside the bounds of the array. Fix: the last position is Length - 1, or use ^1.
  2. Calling a string method without saving the result. name.Trim(); leaves name unchanged. Fix: name = name.Trim();.
  3. Comparing codes with == when case may differ. Fix: string.Equals with StringComparison.OrdinalIgnoreCase.
  4. Building text in a loop with +=. Slow, and it gets slower with every row. Fix: StringBuilder.
  5. Sorting one of two parallel arrays. Marks move to the wrong names. Fix: keep related values together in one object (Module 7).

In real projects​

From 16 years of .NET projects

A school's export button "sometimes timed out". It built a CSV of every student with += in a loop. Small schools were fine; the largest school had 4,000 students and took over a minute.

Changing one variable to a StringBuilder brought it under a second. No new server, no caching, just the right tool.

The same review found user names compared with ==. Users who typed capital letters could not log in. Two small string rules fixed two support tickets a week.

Best practices​

  • Use Length - 1 or ^1 for the last item, never a hard-coded number.
  • Prefer foreach when you do not need the position, and for when you do.
  • Use interpolation with format specifiers for readable, aligned output.
  • Compare codes and emails with StringComparison.OrdinalIgnoreCase.
  • Use StringBuilder whenever you build text inside a loop.

Recap​

  • An array holds a fixed number of items; positions start at 0.
  • ^1 is the last item; 1..4 is a range that excludes position 4.
  • Strings never change; methods return new strings, so save the result.
  • Split, Join, Trim, Contains, and format specifiers handle everyday text work.
  • StringBuilder built 10,000 lines in 2 ms; += took over 600 ms.

Homework​

Library module task: Put three book titles in one array and their available copies in another. Search the titles for BOOK, ignoring case, and print each match with its copies.

Then print a shelf code for every book: BK-, a four-digit number starting at 0001, and the first three letters of the title in capitals.

A correct result looks like:

Output
Search: BOOK
Found: The Jungle Book (0 available)
BK-0001-WIN Wings of Fire
BK-0002-THE The Jungle Book
BK-0003-MAL Malgudi Days
Show the solution
library-assignment/033-library-search.cs
// Library homework, Lesson 33: search titles and build shelf codes with arrays and strings.
// Run with: dotnet run library-assignment/033-library-search.cs
string[] titles = { "Wings of Fire", "The Jungle Book", "Malgudi Days" };
int[] copiesAvailable = { 1, 0, 1 };

// 1. Case-insensitive search.
string search = "BOOK";
Console.WriteLine($"Search: {search}");
for (int i = 0; i < titles.Length; i++)
{
if (titles[i].Contains(search, StringComparison.OrdinalIgnoreCase))
{
Console.WriteLine($" Found: {titles[i]} ({copiesAvailable[i]} available)");
}
}

// 2. Shelf codes: BK-, a 4-digit number, and the first 3 letters of the title in capitals.
for (int i = 0; i < titles.Length; i++)
{
string code = $"BK-{i + 1:D4}-{titles[i][..3].ToUpper()}";
Console.WriteLine($"{code} {titles[i]}");
}

Contains with StringComparison.OrdinalIgnoreCase finds "Book" when you search for "BOOK". titles[i][..3] takes the first three characters of the title.

Quiz​

  1. An array has 6 items. What is the position of the last item?

    1. 6
    2. 5
    3. 1
    4. -1
    Show answer

    Answer: B. 5 Positions start at 0, so the last is Length - 1, which is 5. ^1 also works.

  2. Which names does names[1..4] return from Aarav, Diya, Kabir, Meera, Rohan, Sana?

    1. Aarav to Meera
    2. Diya, Kabir, Meera
    3. Diya to Rohan
    4. Kabir, Meera, Rohan
    Show answer

    Answer: B. Diya, Kabir, Meera A range includes the start position and excludes the end position.

  3. After name.Trim(); (without assignment), what is in name?

    1. The trimmed text
    2. The original text, unchanged
    3. An empty string
    4. null
    Show answer

    Answer: B. The original text, unchanged Strings never change; Trim returns a new string that was thrown away.

  4. What does {serial:D5} print for serial = 42?

    1. 42
    2. 42.00000
    3. 00042
    4. D542
    Show answer

    Answer: C. 00042 D5 pads a whole number with zeros to at least five digits.

  5. Why is += slow for building a 10,000-line file?

    1. Strings are stored on disk
    2. Each += copies all the text built so far into a new string
    3. Loops are slow in C#
    4. Interpolation is slow
    Show answer

    Answer: B. Each += copies all the text built so far into a new string Strings never change, so each += creates a bigger copy; StringBuilder appends in place.

Interview questions​

Questions are grouped by experience level. Click a question to see a model answer. All questions: C# interview questions.

Fresher (0-2 yrs)​

F What is an array, and where do its positions start? concept

A fixed-size collection of items of one type, stored under one name. Positions (indexes) start at 0, so the last item is at Length - 1, also written ^1.

See the example · Permalink

F What happens when you read an array position that does not exist? concept

The program throws IndexOutOfRangeException: Index was outside the bounds of the array. For a six-item array, valid positions are 0 to 5.

See the example · Permalink

F What does string interpolation do? concept

$"..." inserts values into text inside braces, such as $"Topper: {name}". Format specifiers control the output: {average:F2} for two decimals, {serial:D5} for zero padding, {name,-8} for column width.

See the example · Permalink

Mid (3-5 yrs)​

M What does it mean that strings are immutable? concept

A string can never change after it is created. Methods such as Trim and ToUpper return a new string, so name.Trim(); without assigning the result does nothing. Immutability makes strings safe to share between threads and use as dictionary keys.

See the example · Permalink

M When should you use StringBuilder instead of +? concept

When building text in a loop. Each += copies all the text so far into a new string, so the cost grows with every line; StringBuilder appends to one buffer. In the SMS, 10,000 CSV lines took about 620 ms with += and 2 ms with StringBuilder.

See the example · Permalink

M How do you compare two strings while ignoring upper and lower case? coding

string.Equals(a, b, StringComparison.OrdinalIgnoreCase), or methods like Contains and StartsWith with a StringComparison argument. Avoid a.ToUpper() == b.ToUpper(), which creates extra strings and can behave differently across cultures.

See the example · Permalink

Senior (6-9 yrs)​

S An export of all students sometimes times out for the largest school. What would you look for? scenario

How the file text is built. Building it with += in a loop has a cost that grows with the square of the size, so small schools are fine and big ones time out. Switch to StringBuilder or write lines straight to a stream, then measure with production-sized data.

How to answer: Explain why the problem grows faster than the data, how you would prove it with a timer or profiler, and the fix. Mention streaming large files instead of holding them in memory.

See the example · Permalink

S What are the risks of parallel arrays, and what is the better design? design

Two arrays kept in step, such as names and marks, break when one is sorted, filtered, or resized without the other, so values attach to the wrong student. Keep related values together in one object, such as a Student class or record, in one collection.

How to answer: Show the failure concretely (sorting marks only), then the object-based design. Mention that this is the step from procedural code to object-oriented code.

See the example · Permalink

Architect / Lead (10+ yrs)​

A What should a team standardize about string handling in a multi-school, multi-language system? design

Ordinal, case-insensitive comparison for codes, user names, and emails; culture-aware formatting only for text shown to people; a fixed format such as yyyy-MM-dd for files and APIs; Unicode everywhere (NVARCHAR, UTF-8); and StringBuilder or streaming for large output.

How to answer: Separate machine-facing text (codes, files, APIs: invariant and ordinal) from human-facing text (names, dates on screen: culture-aware). Give one bug each rule prevents.

See the example · Permalink

Resources​

  • Code for this lesson: git tag lesson-33-arrays-strings (code repository coming soon)
  • Files: src/SchoolApp.Console/Program.cs, library-assignment/033-library-search.cs
  • Video: coming soon

What's next​

Your program is getting long, and some work, such as finding a grade, appears more than once. Next: methods, which give a piece of work a name so you can reuse it. Lesson 34: Methods and parameters

Want live sessions, code reviews, and interview preparation? Join the next batch at Sahasra Technologies.