Your notebook will hold lots of notes, and you don’t know in advance how many. Creating a separate variable for every note won’t work. You need one variable that holds as many values as you like: a list.
Creating and filling a list
// shopping.cs
List<string> shopping = ["bread", "milk"];
shopping.Add("apples");
Console.WriteLine($"There are {shopping.Count} things on the list.");
Console.WriteLine(
$"The first is {shopping[0]}, the last is {shopping[shopping.Count - 1]}.");
foreach (string item in shopping)
{
Console.WriteLine($"- {item}");
}
There are 3 things on the list.
The first is bread, the last is apples.
- bread
- milk
- apples
One step at a time:
List<string>is the type “list of strings,” a list of texts. The angle brackets say what goes in the list. A list of whole numbers would beList<int>.- The square brackets hold the items the list starts with. You write an empty list as
[]. Addadds an item at the end.Countis the number of items.shopping[0]is the first item. Lists count from 0, not from 1. That’s why the last item has the numberCount - 1.- The second
Console.WriteLineis long, so it runs over two lines. C# doesn’t mind: a statement ends at its semicolon, not at the end of a line. foreachgoes through the list from front to back. In each round, the current item is in the variableitem.
If you ask for a number that doesn’t exist, such as shopping[3] with three items, the program stops with an
ArgumentOutOfRangeException. So check numbers against Count first.
Numbering with for
People count from 1, lists from 0. If you want to show items with numbers, use for and add one:
// numbered.cs
List<string> tasks = ["File taxes", "Fix the bike", "Call Grandma"];
for (int i = 0; i < tasks.Count; i++)
{
Console.WriteLine($"{i + 1}. {tasks[i]}");
}
1. File taxes
2. Fix the bike
3. Call Grandma
The condition is i < tasks.Count, not <=. With three items, i runs from 0 to 2, exactly the valid numbers.
Removing items
// remove.cs
List<string> tasks = ["File taxes", "Fix the bike", "Call Grandma"];
tasks.Remove("Fix the bike");
tasks.RemoveAt(0);
foreach (string task in tasks)
{
Console.WriteLine(task);
}
Console.WriteLine(tasks.Contains("Call Grandma"));
Call Grandma
True
Remove looks for an item with exactly this text and removes it. RemoveAt removes the item at a number, here 0, so the first one.
The items behind it move up. Contains tells you whether an item with exactly this text is in the list.
Searching
When you search your notes, “exactly this text” is too strict. You want every item that contains a search word anywhere, no matter
whether it’s written in upper or lower case. Contains can do that for a single text, too:
// search.cs
List<string> notes = ["Dentist on Tuesday", "Buy bread",
"Day trip to the lake", "Gift for Tom"];
string search = "day";
int matches = 0;
foreach (string note in notes)
{
if (note.Contains(search, StringComparison.OrdinalIgnoreCase))
{
Console.WriteLine(note);
matches++;
}
}
Console.WriteLine($"{matches} matches for \"{search}\".");
Dentist on Tuesday
Day trip to the lake
2 matches for "day".
note.Contains(search, ...) asks whether the search word occurs in this one text. StringComparison.OrdinalIgnoreCase says: “Ignore
upper and lower case.” The name is long, but your editor completes it as soon as you type the first few letters.
Besides lists, there are arrays, written like string[] days = ["Mon", "Tue", "Wed"];. They work in a similar way but have a fixed
length. For data that should grow, a list is the better choice. You’ll meet arrays again in lesson 10.
Exercise 1
Write names.cs. The program keeps asking for names until it gets an empty line, and collects them in a list. Then it prints how many
names there are and lists them with numbers.
Solution 1
// names.cs
List<string> names = [];
while (true)
{
Console.Write("Name (blank = done): ");
string name = Console.ReadLine() ?? "";
if (name == "")
{
break;
}
names.Add(name);
}
Console.WriteLine($"{names.Count} names:");
for (int i = 0; i < names.Count; i++)
{
Console.WriteLine($"{i + 1}. {names[i]}");
}
Name (blank = done): Ada
Name (blank = done): Linus
Name (blank = done): Grace
Name (blank = done):
3 names:
1. Ada
2. Linus
3. Grace
The list is created before the loop so that it still exists after the loop, just like the sum in lesson 6.
Exercise 2
Remember the instructions for the literal-minded helper from lesson 1? Now you write them in C#. Take this list:
List<int> numbers = [4, 17, 9, 23, 8];
Write largest.cs, which finds the largest number and prints it, without ready-made tools, using only a loop and an if. Then print
the average as well.
Solution 2
// largest.cs
List<int> numbers = [4, 17, 9, 23, 8];
if (numbers.Count == 0)
{
Console.WriteLine("The list is empty.");
}
else
{
int largest = numbers[0];
int sum = 0;
foreach (int number in numbers)
{
if (number > largest)
{
largest = number;
}
sum += number;
}
double average = (double)sum / numbers.Count;
Console.WriteLine($"The largest number is {largest}.");
Console.WriteLine($"The average is {average}.");
}
The largest number is 23.
The average is 12.2.
Step by step, these are the instructions from lesson 1, including the empty list. For the average, you divide the sum (61) by the count
(5). If both were int, the integer division from lesson 4 would give you just 12. (double)sum first turns the sum into a number with
decimal places. As in lesson 4, a computer set to another language, German for example, may show 12,2 with a comma instead of 12.2.
C# also comes with ready-made tools for jobs like this, such as numbers.Max(). You’ll get to know them in the follow-up course. But
once you’ve written it yourself, you know what goes on inside.
Exercise 3
Write search-list.cs. It starts with a list of five notes of your choice, asks for a search word and shows every note that contains
it, no matter whether it’s written in upper or lower case. If nothing matches, it prints “Nothing found.”
Solution 3
// search-list.cs
List<string> notes = ["Dentist on Tuesday", "Buy bread", "Day trip to the lake",
"Gift for Tom", "Change the tires"];
Console.Write("What are you looking for? ");
string search = Console.ReadLine() ?? "";
bool found = false;
foreach (string note in notes)
{
if (note.Contains(search, StringComparison.OrdinalIgnoreCase))
{
Console.WriteLine($"- {note}");
found = true;
}
}
if (!found)
{
Console.WriteLine("Nothing found.");
}
What are you looking for? buy
- Buy bread
Instead of a counter, a bool variable remembers here whether there was at least one match. Both are correct. What happens if you type
nothing at all and press Enter right away? An empty text is contained in every text, so all the notes appear. For a search, that’s
actually handy. In the notebook, you’ll catch it anyway.
What you can do now
- You create a
List<string>or aList<int>, an empty one too, with[]. - You add with
Add, count withCount, read with[number]and remove withRemoveandRemoveAt. - You know that lists count from 0, and you still show numbers starting at 1.
- You go through a list with
foreachandfor. - You search with
ContainsandStringComparison.OrdinalIgnoreCase. - You’ve written your first algorithm yourself.
Read more
- Data collections: lists in Microsoft’s tutorial series, with sorting and searching.
- Store and iterate through sequences of data using Arrays and the foreach statement in C#:
a module on arrays and
foreach.