Arrays and Strings: Marks, Names, Admission Numbers – C# | Lesson 33
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
decimal[] marks = { 92m, 85m, 67m, 95m, 58m, 78m };
| Code | Result | Meaning |
|---|---|---|
marks.Length | 6 | How many items |
marks[0] | 92 | The first item; positions start at 0 |
marks[5] | 78 | The last item, at position Length - 1 |
marks[^1] | 78 | Index from the end: ^1 is the last item |
names[1..4] | Diya, Kabir, Meera | A 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:
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
| Method | Example | Result |
|---|---|---|
Trim() | " rajesh sharma ".Trim() | "rajesh sharma" |
ToUpper() / ToLower() | "rajesh".ToUpper() | "RAJESH" |
Length | "rajesh sharma".Length | 13 |
StartsWith / Contains | "rajesh".StartsWith("raj") | true |
Split | "ADM-2017-0003".Split('-') | ["ADM", "2017", "0003"] |
string.Join | string.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 write | You 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 braces | Meaning | Example result |
|---|---|---|
{average:F2} | Two decimal places | 79.17 |
{serial:D5} | At least 5 digits, padded with zeros | 00042 |
{name,-8} | 8 characters wide, aligned left | Aarav |
{mark,5} | 5 characters wide, aligned right | 92 |
{date:yyyy-MM-dd} | A date format | 2026-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
// 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}");
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:
| Method | Run 1 | Run 2 | Run 3 | Same text |
|---|---|---|---|---|
+= | 618 ms | 676 ms | 672 ms | Yes |
StringBuilder | 2 ms | 2 ms | 2 ms | Yes |
Rule of thumb: joining a few strings with + or interpolation is fine. Building text in a
loop needs a StringBuilder.
Common mistakes
- Reading past the end of an array.
marks[6]on a six-item array stops the program withIndexOutOfRangeException: Index was outside the bounds of the array.Fix: the last position isLength - 1, or use^1. - Calling a string method without saving the result.
name.Trim();leavesnameunchanged. Fix:name = name.Trim();. - Comparing codes with
==when case may differ. Fix:string.EqualswithStringComparison.OrdinalIgnoreCase. - Building text in a loop with
+=. Slow, and it gets slower with every row. Fix:StringBuilder. - 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
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 - 1or^1for the last item, never a hard-coded number. - Prefer
foreachwhen you do not need the position, andforwhen you do. - Use interpolation with format specifiers for readable, aligned output.
- Compare codes and emails with
StringComparison.OrdinalIgnoreCase. - Use
StringBuilderwhenever you build text inside a loop.
Recap
- An array holds a fixed number of items; positions start at 0.
^1is the last item;1..4is 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.StringBuilderbuilt 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:
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 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
An array has 6 items. What is the position of the last item?
Show answer
Answer: B. 5 Positions start at 0, so the last is Length - 1, which is 5. ^1 also works.
Which names does names[1..4] return from Aarav, Diya, Kabir, Meera, Rohan, Sana?
Show answer
Answer: B. Diya, Kabir, Meera A range includes the start position and excludes the end position.
After name.Trim(); (without assignment), what is in name?
Show answer
Answer: B. The original text, unchanged Strings never change; Trim returns a new string that was thrown away.
What does {serial:D5} print for serial = 42?
Show answer
Answer: C. 00042 D5 pads a whole number with zeros to at least five digits.
Why is += slow for building a 10,000-line file?
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.