List in C# is one of the most commonly used collection types for storing a dynamic group of values. It is useful when you want an ordered collection that can grow or shrink during program execution without manually managing array size.
Unlike an array, a List<T> does not require you to know the final size in advance. You can add items, remove items, insert values in the middle, search for elements, sort the data, and iterate through it using the normal looping patterns available in C#.
Because of that flexibility, lists appear in desktop applications, ASP.NET Core APIs, services, game development, business logic layers, and data processing code. If you are writing normal application code in C#, there is a high chance you will use List<T> frequently.
What Is List in C#?
List<T> is a generic collection class from the System.Collections.Generic namespace. It stores elements of the same type in an ordered sequence and supports dynamic resizing.
using System.Collections.Generic;
List<int> numbers = new List<int>();
Here, List<int> creates a list that stores integers. The type parameter inside angle brackets defines what kind of data the list can contain.
Why Use List Instead of Array?
An array has a fixed size after creation, while a list can expand automatically as you add more items. That makes a list easier to use when the number of elements is not known at compile time or when items must be inserted and removed often.
Under the hood, a list still uses an array internally, but it manages resizing for you. That means you get array-like indexed access with a more convenient programming model for day-to-day application work.
Syntax of List in C#
List<DataType> listName = new List<DataType>();
You can also initialize the list with values during creation.
List<string> names = new List<string>
{
"Aarav",
"Diya",
"Kabir"
};
This collection initializer syntax is common because it is readable and compact.
Creating a List in C#
Lists can be created empty or with predefined values.
List<int> marks = new List<int>();
List<int> scores = new List<int> { 85, 90, 95 };
An empty list is useful when values will be added later. A prefilled list is useful when the starting items are already known.
Adding Elements to a List
The most basic operation is adding elements. The Add() method appends one value to the end of the list.
List<string> fruits = new List<string>();
fruits.Add("Apple");
fruits.Add("Banana");
fruits.Add("Mango");
If you need to add multiple values at once, use AddRange().
fruits.AddRange(new List<string> { "Orange", "Pineapple" });
Accessing List Elements
Like arrays, lists use zero-based indexing. The first element is at index 0, the second at index 1, and so on.
List<int> numbers = new List<int> { 10, 20, 30 };
Console.WriteLine(numbers[0]); // 10
numbers[1] = 99;
Console.WriteLine(numbers[1]); // 99
This direct indexed access makes lists convenient when order matters and you need fast lookup by position.
Inserting Elements in a List
You can insert an element at a specific position by using Insert(). Elements after that index shift one position to the right.
List<int> values = new List<int> { 1, 2, 4 };
values.Insert(2, 3);
After insertion, the list becomes 1, 2, 3, 4. This is useful when order matters and new values must appear in a specific location.
Removing Elements from a List
Lists provide multiple ways to remove items depending on whether you know the value, the index, or a logical condition.
| Method | Purpose |
|---|---|
Remove(value) | Removes the first matching value |
RemoveAt(index) | Removes the element at a specific index |
RemoveAll(condition) | Removes all matching elements |
Clear() | Removes all elements |
List<string> names = new List<string> { "Aarav", "Diya", "Kabir", "Diya" };
names.Remove("Diya");
names.RemoveAt(1);
names.RemoveAll(name => name.StartsWith("K"));
These methods make lists much more convenient than fixed arrays when element management changes often.
Count and Capacity in List
The Count property tells you how many elements are currently inside the list. The Capacity property tells you how much space the internal storage currently has before another resize is needed.
List<int> data = new List<int> { 10, 20, 30 };
Console.WriteLine(data.Count); // 3
Console.WriteLine(data.Capacity); // implementation-dependent value
In normal business code, Count is used far more often than Capacity. Capacity becomes more relevant when thinking about performance and reducing repeated internal resizing.
Iterating Through a List
You can iterate through a list with for, foreach, or even LINQ in more advanced scenarios.
List<int> numbers = new List<int> { 5, 10, 15 };
for (int i = 0; i < numbers.Count; i++)
{
Console.WriteLine(numbers[i]);
}
foreach (int number in numbers)
{
Console.WriteLine(number);
}
Use for when you need the index. Use foreach when you only need the values and want the simplest iteration style.
Searching in a List
Lists provide several search helpers such as Contains(), IndexOf(), Find(), and Exists().
List<string> cities = new List<string> { "Delhi", "Mumbai", "Pune" };
bool hasPune = cities.Contains("Pune");
int index = cities.IndexOf("Mumbai");
string found = cities.Find(city => city.StartsWith("D"));
These helpers improve readability because they describe intent directly instead of forcing you to write manual loops for every search operation.
Sorting and Reversing a List
Lists can be sorted and reversed in place.
List<int> values = new List<int> { 40, 10, 30, 20 };
values.Sort();
values.Reverse();
You can also provide custom comparison logic when sorting objects or when default ordering is not enough.
List of Objects in C#
One of the most common real-world uses of a list is storing objects.
class Student
{
public string Name { get; set; }
public int Age { get; set; }
}
List<Student> students = new List<Student>
{
new Student { Name = "Aarav", Age = 20 },
new Student { Name = "Diya", Age = 21 }
};
This pattern is everywhere in application development because most real business data is modeled as objects instead of primitive values.
Passing List to Methods
When you pass a list to a method, the method receives a reference to the same list object. That means adding or removing items inside the method changes the original list contents.
static void AddValue(List<int> data)
{
data.Add(100);
}
List<int> numbers = new List<int> { 1, 2, 3 };
AddValue(numbers);
Console.WriteLine(numbers.Count); // 4
This is important because even though you are not using the ref keyword, the list object itself is still shared between caller and method.
List vs Array in C#
| Point | List<T> | Array |
|---|---|---|
| Size | Dynamic | Fixed |
| Add and remove | Easy | Manual handling needed |
| Indexed access | Yes | Yes |
| Namespace | System.Collections.Generic | Built-in array syntax |
| Best use case | Changing collections | Known fixed-size data |
Choose a list when the collection size may change. Choose an array when the size is fixed and you want the simplest fixed storage model.
Performance Notes for List
Lists are efficient for appending values to the end and for indexed access. However, inserting or removing items in the middle can be more expensive because later elements may need to shift.
If you know a list will store many items, you can provide an initial capacity to reduce repeated internal resizing.
List<int> data = new List<int>(1000);
This does not set the count to one thousand. It simply reserves internal space for expected growth.
Common Mistakes with List in C#
- Forgetting to include
using System.Collections.Generic;. - Accessing an invalid index and causing
ArgumentOutOfRangeException. - Assuming
Capacitymeans the current item count. - Modifying a list inside a
foreachloop and causing runtime errors. - Using a list when a dictionary or set would better match the lookup pattern.
Understanding these mistakes helps avoid common debugging issues in beginner and intermediate C# code.
Best Practices for List in C#
- Use
List<T>when collection size may change at runtime. - Use meaningful element types instead of storing unrelated values in one list.
- Check indexes before direct access when input may be unsafe.
- Use
foreachfor read-only iteration andforwhen indexes are needed. - Set initial capacity when large predictable growth is expected.
List in Real Applications
In real applications, lists are used for API result sets, collections of users, lists of orders, menu items, log entries, tasks, file paths, and query results. They are one of the default collection choices when the data must stay ordered and item count is not constant.
That is why understanding list operations properly is more important than treating them as just another syntax topic. Good use of List<T> makes business code simpler, cleaner, and easier to maintain.
List Interview Points
For interviews, remember that List<T> is a generic dynamic collection, ordered, zero-based, and stored in the System.Collections.Generic namespace. Common comparisons include list vs array, count vs capacity, and how insertion or removal affects performance.
You should also remember that passing a list to a method shares the same underlying list object, and that modifying a collection during foreach iteration is a common source of runtime problems.
FAQs on List in C#
What is List in C#?
List<T> is a generic collection that stores elements of the same type in an ordered sequence and grows or shrinks dynamically.
What is the difference between List and Array in C#?
An array has fixed size after creation, while a list can grow and shrink dynamically and provides richer helper methods.
What is Count in a List?
Count returns the number of actual elements currently stored inside the list.
Can a List store objects in C#?
Yes. One of the most common uses of List<T> is storing custom objects such as students, orders, products, or API models.