Beginner's Guide To C# List Class

Claudio Bernasconi
Claudio Bernasconi
hero image
Want a career in tech?

Take our career path quiz to find the best fit for you and get a personalized step-by-step roadmap 👇

Take The 3-Minute QuizTake The 3-Minute Quiz

Have you ever been working with an array and it all gets out of hand?

You start with a simple collection, then realize you need to add more items, remove a few, maybe sort them, and suddenly arrays feel clunky. 

The good news is there's a solution and that’s where C# Lists come in. They remove most of the pain that comes with arrays, letting you focus on solving problems instead of wrestling with syntax. 

The trick, of course, is knowing how to use them properly. So in this guide, I’ll walk you through everything you need to know to use Lists confidently, from creating one to building a simple project around them, and avoiding the common mistakes.

Sidenote: Want to learn more about C#? Check out my complete C# / .NET course!

It’s the only course you need to learn C# programming and master the .NET platform and Blazor. You’ll learn everything from scratch and put your skills to the test with exercises, quizzes, and projects!

With that out of the way, let’s get into this 5-minute tutorial.

What is the List class and what does it do?

When you first start working in C#, arrays usually feel like the obvious choice for holding multiple values of the same type. Mainly because they’re simple and they work.

The thing is arrays have a catch in that once you decide how many elements they’ll hold, that’s it. You can’t just add more later without creating a brand new array and copying everything over. As you can imagine, this can be a pain to deal with.

This is where the List<T> class comes in. 

A List<T> is basically an array that can grow and shrink whenever you need it to. Add something new, and it makes space. Remove something, and it adjusts. You don’t have to plan the exact size ahead of time because it’s flexible.

It’s almost like an array is a piece of A4 paper. Once you run out of space, you’re stuck. But a List is more like a Google Doc. It can expand as you keep typing or shrink when you delete things, without you doing anything extra.

Make sense?

Another important detail to understand is the <T> part of List<T>. That’s called a generic type parameter, and it just means: “Tell me what type of thing you want to store, and I’ll only accept that type.” 

This means that if you make a List<string>, it can only hold strings, while a List<int> can only hold integers. 

It seems like a limitation, but it's actually good type safety. It also keeps your code clean, executes fast, and prevents messy bugs.

The basic List<T> syntax

So now that we understand what this feature is, let’s take a look at it in code.

Here's the basic syntax:

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        // A list of strings
        List<string> names = new List<string>();

        // A list of integers
        List<int> numbers = new List<int>();
    }
}

What’s happening here is pretty simple. We’re telling C# we want a list, and we’re also telling it what type of data that list should hold. Right now, both lists are empty, but ready to be filled.

And the easiest way to really understand how they work is to use them in an actual example, so let’s do that next.

How to use the List class in C#

We’re going to build a simple to-do list program. 

This is a perfect example because tasks aren’t fixed. You add new ones, cross off old ones, and sometimes check what’s left. That kind of flexibility is provided by Lists and is much more complex to implement using arrays.

For example

If we tried to do this with an array, we’d hit a wall quickly, because arrays force you to pick a size up front.

So let’s say you create one for 5 tasks, but tomorrow you suddenly have 6. Now you’d have to create a brand-new array and copy everything over. But with a List, that problem disappears. You can just keep adding, and it will handle the resizing for you.

Make sense?

So let’s get into creating this.

How to create a List

Let’s start by making an empty List to hold our tasks:

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        // Create a new list of tasks
        List<string> tasks = new List<string>();
    }
}

We’ve set this to strings because we’re only going to use text values like “Buy groceries” or “Call mom.” 

Remember, though, if you tried to add a number or anything else, the compiler would stop you before you even run the program. So make sure you choose the right type that you want to use first!

Right now, our list is empty, but it’s ready to hold any string we want to add, so we’ll do this using Add().

How to add items to your List

So let’s add some tasks using Add()

tasks.Add("Buy groceries");
tasks.Add("Finish C# project");
tasks.Add("Call mom");

Every time you call Add(), C# takes the new item and sticks it at the end of the list. That’s all you have to do and there’s no worrying about running out of space or shifting things around.

The beauty here is flexibility. You could add 3 tasks today, 10 tomorrow, and none the next day. The List just grows or shrinks as you need it to. 

So now that we have our list, we need to be able to do things with the items on it.

How to access items in your List

There are times when you won’t want the whole list, but only one specific item.

For example

Maybe you just want to see the first item at the top of the list so you can get it done and ignore the rest for now.

We can do that like so:

Console.WriteLine(tasks[0]); // Output: Buy groceries

So what’s happening here?

If you don’t know, lists organize their items using an index. However, the first item is at index starts at 0, the second at index 1, and so on.

By writing tasks[0], we’re saying, “show me the item at position 0,” and C# gives us back "Buy groceries".

How to iterate through your List

Accessing one item is handy when you know exactly which one you want, such as checking just the first task so you know where to start. But what if you want to see all the tasks on your list?

Well, you could do this by printing each index one by one:

Console.WriteLine(tasks[0]);
Console.WriteLine(tasks[1]);
Console.WriteLine(tasks[2]);

That works for three items, but what if you had 30? Or 300? Writing all those lines would be a nightmare.

That’s where loops come in. 

A foreach loop will automatically walk through every item in your list, no matter how many there are:

foreach (string task in tasks)
{
    Console.WriteLine(task);
}

This prints each task in order, one after the other. Add more tasks, remove some, shuffle them around and it doesn’t matter. The loop always adjusts to whatever is in the list at that moment.

Hint: 

We can use the foreach statement for every type implementing the IEnumerable or IEnumerable<T> interface.

Speaking of removing items…

How to remove items from your List

Once you’ve finished a task, you don’t really want it sitting on your list anymore. That’s where Remove() and RemoveAt() come in.

For example

If you know the name of the item you want to clear out, you can remove it by value with Remove():

tasks.Remove("Call mom");

This will look through the list, find “Call mom,” and take it out.

Let's be honest, though. Even though this is just a basic to-do list app, you probably still don't want to have to write the item each time you want to delete it because that's extra work. 

It’s more likely, you would want to remove something at a certain position. That’s what RemoveAt() is for.

For example

Let's say that you just finished the first thing on your list, you could then set it to remove that first item and 'tick it off'.

tasks.RemoveAt(0); // removes "Buy groceries"

This then removes our top item, which in this case was our grocery shopping.

Better still? 

After removing, the List automatically closes the gap so you don’t end up with empty spaces. Everything shifts down, keeping the list nice and tidy.

How to find out how many items are in your list

Sometimes it’s not about the items themselves, but about knowing how many you’ve got left to deal with. That’s what the Count property is for.

For example

Say you’ve been chipping away at your to-do list and you just want a quick reminder of how many tasks are still sitting there. Well, instead of going through the list one by one, Count tells you instantly:

Console.WriteLine("You have " + tasks.Count + " tasks left.");

If your list currently has 3 items, Count will return 3. Cross one off with Remove() or RemoveAt(), and Count automatically drops to 2.

Why is this useful?

Because in real programs, you’ll often want to give users feedback or even make decisions based on how many items are in a list. 

For example

You could:

  • Warn the user when their shopping cart is empty

  • Show a message when a to-do list is all finished

  • Limit the number of players who can join a game lobby

And the best part is you don’t have to manually keep track of this number yourself. The List does all the bookkeeping in the background and just hands you the result.

Handy, right?

Storing custom objects in Lists

Besides using simple types, such as string or int, we can also use our custom objects and store them in a List<T>.

For example

Let’s say we have a complex type, such as a Person class, we can create a List to store all the employees working on a project:

var employees = new List<Person>();
employees.Add(new Person("John", "Doe"));

In this simple example, the Person class has first and last name properties that we can set in the type’s constructor. We create an object and add it to the list.

Other list options

By now, you know the everyday things you’ll do with a List: adding, removing, checking items, and looping through them. That stuff is the bread and butter and what you’ll use most days.

However, the List class actually comes with a whole toolbox of extra methods that you can pull out when you need them. Think of these like shortcuts: you won’t use them constantly, but when the situation arises, they can save you time and make your code a lot cleaner.

Let’s go through a few of the most useful ones.

How to check if something is already in your list

Sometimes you’ll want to know if an item is already on the list before you add it. Otherwise, you might end up with duplicates you didn’t mean to have. That’s what the Contains() method is for.

For example

Let’s say our list is a shopping list and we want to check if “Milk” is already there before adding it again. 

We could do that like this:

if (tasks.Contains("Milk"))
{
    Console.WriteLine("Milk is already on the list!");
}
else
{
    tasks.Add("Milk");
    Console.WriteLine("Milk added to the list.");
}

Simple enough, right? 

What’s cool is you don’t have to hard-code this for every possible item because Contains() checks whatever value you pass in.

So if you asked about “Eggs,” it would look through the list for “Eggs” and respond accordingly:

  • If “Eggs” is there, you’ll see “Eggs are already on the list!”

  • If “Eggs” isn’t there, it gets added, and you’ll see “Eggs added to the list”

This makes your program flexible because the same logic works for any item the user types in.

Hint:

If uniqueness is essential in your use case, consider using a HashSet<T> as your collection type. A HashSet is a collection of unique elements and doesn’t allow duplicates. However, it doesn’t provide item ordering.

How to find the position of an item in your list

Sometimes it’s not enough to know if something is in your list. Sometimes you also want to know where it is. That’s what the IndexOf() method is for. 

Think of it as a search tool that tells you the position of an item.

For example

Let’s say we want to find “Milk” in our shopping list:

int index = tasks.IndexOf("Milk");

if (index != -1)
{
    Console.WriteLine($"Milk is at position {index} in the list.");
}
else
{
    Console.WriteLine("Milk is not on the list.");
}

If “Milk” is found, IndexOf() gives you its index (remember, lists start at 0). If it’s not found, it returns -1.

Why does this matter? 

Because once you know where something is, you can do more with it.

You might:

  • Replace “Milk” with another option. Perhaps we swap it for “Oat Milk”

  • Or if it wasn’t a shopping list, maybe we might want to highlight it in a UI by pointing to that position

  • Or even queue up the next item after it (think songs in a playlist).

IndexOf() is less about just printing where something is and more about giving you the position so you can act on it however you need.

How to organize your list with Sort() and Reverse()

So far, we’ve been working with items one at a time, but sometimes you’ll want to organize the whole list. Two methods that help with this are Sort() and Reverse(). So let’s take a look at each of them.

Sort() arranges everything in ascending order. This means that it sorts text alphabetically, or for numbers it sorts by smallest to largest.

For example

If we have our current shopping list from earlier, we can alphabetize it like so:

List<string> tasks = new List<string>
{
    "Walk the dog",
    "Buy groceries",
    "Call mom"
};

tasks.Sort();

foreach (string task in tasks)
{
    Console.WriteLine(task);
}

This would then output:

Buy groceries
Call mom
Walk the dog

Nice and neat.

We can also use Reverse() to flip the current order of the list.

For example

Let’s say that you have a video game, and you want to rank the highest scores from largest to smallest. With Sort(), it would rank them from lowest to smallest.

But Reverse() would organize it, but then flip that so we see the largest number and thus the highest score at the top.

List<int> scores = new List<int> { 50, 200, 120, 80 };

// Sort from lowest to highest
scores.Sort();

// Reverse so the highest scores are first
scores.Reverse();

foreach (int score in scores)
{
    Console.WriteLine(score);
}

This then gives the highest score:

200
120
80
50

How to insert an item at a specific position in your list

So far, we’ve been adding items to the end of a list with Add(), but sometimes, the order really matters, and you’ll want to put something at the top or right in the middle instead of the end.

That’s what Insert() is for.

For example

Let’s say you’re building a to-do list and suddenly remember something urgent, like paying a bill comes up, and so you need to prioritize this at the top of the list before everything else. 

We could do that like so:

List<string> tasks = new List<string>
{
    "Buy groceries",
    "Call mom",
    "Walk the dog"
};

// Insert urgent task at the very top (index 0)
tasks.Insert(0, "Pay electricity bill ASAP");

foreach (string task in tasks)
{
    Console.WriteLine(task);
}

Output:

Pay electricity bill ASAP
Buy groceries
Call mom
Walk the dog

By giving Insert() an index (in this case 0) and a value, you tell the list exactly where the new item should go. 

Also, everything else shifts down automatically, so nothing gets overwritten.

How to clear your list

Sometimes, you don’t just want to remove one or two items. Perhaps you want to wipe the slate clean and start fresh. That’s what the Clear() method does.

Think of it like erasing a whiteboard. Everything that was on there is gone, and you’re left with a completely empty list that’s ready to be used again.

For example

List<string> tasks = new List<string>
{
    "Buy groceries",
    "Call mom",
    "Walk the dog"
};

tasks.Clear();

Console.WriteLine("Tasks left: " + tasks.Count);

This will then clear the whole list and let you know it’s empty like so:

Tasks left: 0

This is especially useful when you’re reusing the same list in a program but don’t want to create a brand new one every time. 

Perhaps you did all Monday's tasks and now you want to fill this out for Tuesday etc.

Time to give List a try in your own code!

As you can see, Lists take the stress out of managing data in C#. Instead of worrying about array sizes, you can focus on the actual problem you’re solving.

The best way to really understand them is to play around with them!

So why not try building your own mini to-do list app, experiment with adding and removing items, or test out Sort() and Reverse() on a list of numbers. The more you practice, the more natural Lists will feel.

Fire up your editor, write some code, and see what you can build.

P.S.

Don’t forget. If you want to learn more about C# and the .NET platform, then check out my complete course:

It’s the only course you need to learn C# programming and master the .NET platform. You’ll learn everything from scratch and put your skills to the test with exercises, quizzes, and projects!

Plus, once you join, you'll have the opportunity to from me, other students and working tech professionals.

Best articles. Best resources. Only for ZTM subscribers.

If you enjoyed Claudio's post and want to get more like it in the future, subscribe below. By joining over 500,000 ZTM email subscribers, you'll receive exclusive ZTM posts, opportunities, and offers.

No spam ever, unsubscribe anytime

Check out all of my other guides below:

More from Zero To Mastery

5 Reasons Why You Should Learn C# preview
5 Reasons Why You Should Learn C#
8 min read

It's been 23 years since C# went live, but it's still growing in popularity. Find out why (+ why you should learn this language ASAP to advance your career).

Best Programming Languages To Learn In 2025 preview
Best Programming Languages To Learn In 2025
21 min read

Want to know the best programming languages to learn in 2025? See the top 10 picks for high-paying jobs, future-proof skills, and unstoppable growth.

What Is C# Used For? Pretty Much Everything! preview
What Is C# Used For? Pretty Much Everything!
8 min read

C# can build anything from Desktop + Web Apps to Cloud Integrations, Games, Automations + more. In this guide, I walk through why it works so well in each area.