1. Introduction
So, imagine this: you have a collection... and you need to quickly grab the third, seventh, or, say, the zero-th element. Or swap them around. In an array, that's easy — by index (array[3]). But what about collections? Not all collections are equally indexable!
That's where the IList<T> interface comes in — a universal contract that requires a collection to support working with elements by index. In short: if you have a collection that implements IList<T>, you can safely access its elements by index (just like an array) and change them on the fly.
Analogy:
Think of a library card: every book has its own number on the shelf, and you can always walk up to the "third in line" book and grab it. That's how a collection that supports IList<T> behaves.
2. General Structure and Methods of IList<T>
The IList<T> interface is the hero of many collections. It extends ICollection<T> (which in turn extends IEnumerable<T>, letting you loop through the collection), and adds the most important thing: index access.
Interface inheritance diagram:
IEnumerable<T>
▲
│
ICollection<T>
▲
│
IList<T>
Main members of the IList<T> interface
| Member | Purpose |
|---|---|
|
Get or set an element by index |
|
Find the index of the first occurrence of an element |
|
Insert an element at a given position |
|
Remove an element by index |
All the other members, like Add, Remove, Clear, Contains, come from the ICollection<T> interface.
Key feature: the indexer
The main "thing" about IList<T> is the indexer. It's that syntactic sugar that lets you write:
var myList = new List<int> { 10, 20, 30 };
int secondValue = myList[1]; // Gets 20
myList[2] = 42; // Changes the third element
3. Which collections implement IList<T>
In the standard .NET library, a bunch of familiar structures support the IList<T> interface. Let's check out the most popular ones:
| Collection | Indexable | Description |
|---|---|---|
|
Yes | Dynamic array |
|
Yes | Regular arrays are indexable |
|
Yes | Used for data binding |
|
Yes | List with notifications |
|
Yes | Base class for collections |
Heads up:
LinkedList<T> and
HashSet<T>
DO NOT implement
IList<T>, because they don't have fast indexing (yep,
LinkedList<T> doesn't have
list[5]!).
4. Examples of using IList<T>
Getting and setting by index
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
IList<string> fruits = new List<string> { "Apple", "Banana", "Pear" };
// Get the second element
string fruit = fruits[1];
Console.WriteLine(fruit); // Banana
// Replace the third element
fruits[2] = "Orange";
Console.WriteLine(fruits[2]); // Orange
}
}
Insert and remove by index
fruits.Insert(1, "Kiwi"); // Insert "Kiwi" at the second position
// List is now: "Apple", "Kiwi", "Banana", "Orange"
fruits.RemoveAt(0); // Remove the first element ("Apple")
// List is now: "Kiwi", "Banana", "Orange"
Finding the index of an element
int index = fruits.IndexOf("Orange"); // Returns index (2) or -1 if not found
if (index != -1)
Console.WriteLine("Orange is at position: " + index);
else
Console.WriteLine("Orange not found");
5. Implementation quirks and common mistakes
When working with IList<T>, it's easy to fall into a couple of "traps," especially if you forget that indexing starts at zero, and the collection's length is the actual number of elements.
For example, trying to access a non-existent element:
Console.WriteLine(fruits[100]); // IndexOutOfRangeException!
Indexes in C# are like egg-laying hens: they start at zero, not one. If your list has 4 elements, the last available index is 3.
Also, keep in mind that not all IList<T> implementations are equally fast. For example, arrays or List<T> have instant index access (O(1)), but if you made your own collection based on a linked list and for some reason slapped IList<T> on it, the operation could get slow. Though the standard library doesn't do that.
And another thing: if you work with an array as an IList<T>, you can change elements, but you can't change the array's size. Methods like Add, Remove, Insert, etc. for arrays will throw a NotSupportedException.
int[] myArray = { 1, 2, 3 };
IList<int> listView = myArray; // Upcast
listView[0] = 42; // Works!
listView.Add(99); // Throws NotSupportedException
6. Practical use and why you need this
In real projects, almost every other collection is something that implements IList<T>, because it's super handy to quickly access elements by number, change them, insert and remove by position. For example:
- Interface properties in WPF or WinForms ViewModel, where UI element lists are bound.
- Implementing sorting, searching, or permutation algorithms where you need index access.
- Import/export modules that work with a dynamic list of objects.
At an interview the question about the differences between IEnumerable<T>, ICollection<T>, and IList<T> is a classic. Knowing what each level is for, you can confidently explain to the interviewer why HashSet<T> doesn't implement IList<T> (because uniqueness is more important than order and indexes!).
GO TO FULL VERSION