The C# programming language provides excellent support for working with collections of data. C# includes several classes and interfaces that can help you query collections of data efficiently. The most commonly used interfaces for working with collections are IEnumerable, ICollection, IList, and IQueryable.
In this article we will examine each of these interfaces and discuss how we can work with them in .NET 8, illustrated with code examples.
Create a console application project in Visual Studio
First off, let’s create a .NET Core console application project in Visual Studio. Assuming Visual Studio 2022 is installed in your system, follow the steps outlined below to create a new .NET Core console application project.
- Launch the Visual Studio IDE.
- Click on “Create new project.”
- In the “Create new project” window, select “Console App (.NET Core)” from the list of templates displayed.
- Click Next.
- In the “Configure your new project” window, specify the name and location for the new project.
- Click Next.
- In the “Additional information” window shown next, choose “.NET 8.0 (Long Term Support)” as the framework version you would like to use.
- Click Create.
We’ll use this .NET 8 console application project to work with the IEnumerable, ICollection, IList, and IQueryable interfaces in the subsequent sections of this article.
The IEnumerable interface in C#
The IEnumerable interface in C# allows you to iterate over a collection of elements. It contains one method called GetEnumerator that returns an instance of type IEnumerable<T>. You can use this instance to iterate a collection of elements in a read-only, forward-only manner. You cannot use IEnumerable to iterate backwards.
The IEnumerable<T> interface is defined in the System.Collections namespace as shown below.
public interface IEnumerable<out T> : IEnumerable { new IEnumerator<T> GetEnumerator(); }
The following code snippet illustrates how you can work with IEnumerable<T> in C#.
List<string> cities = new List<string>() { "New York", "London", "Tokyo", "Lisbon", "Hyderabad", "Chicago" }; IEnumerable<string> query = cities.Where(x => x.StartsWith("L")); foreach (var city in query) { Console.WriteLine(city); }
When you execute the preceding piece of code, the names of the cities that start with the letter “L” will be displayed at the console window.
The ICollection interface in C#
The ICollection interface is the base interface of all classes pertaining to the System.Collections namespace. It provides a generic collection of objects where the elements of the collection are accessible using an index.
The ICollection<T> interface is the generic equivalent of the ICollection interface. It extends the IEnumerable<T> interface, which in turn extends the IEnumerable interface as shown in the code snippet given below.
public interface ICollection<T> : IEnumerable<T> { int Count { get; } bool IsReadOnly { get; } void Add(T item); void Clear(); bool Contains(T item); void CopyTo(T[] array, int arrayIndex); bool Remove(T item); }
Unlike the IEnumerable interface, the ICollection interface allows you to add or remove elements from a collection. The following code snippet shows how you can use the ICollection interface in C#.
ICollection<string> countries = new Collection<string>(); countries.Add("USA"); countries.Add("India"); countries.Add("England"); countries.Add("Japan"); foreach (string country in countries) { Console.WriteLine(country); }
When you execute the above code, the country names added to the collection will be displayed at the console window.
The IList interface in C#
The IList interface is available in the System.Collections namespace. It represents a strongly typed collection in which the elements of the collection are accessible using an index. IList extends the ICollection interface and contains additional methods such as IndexOf, Insert, and RemoveAt for working with collections.
The following code snippet shows how the IList<T> interface is defined in the System.Collections namespace.
public interface IList : ICollection { Object this[int index] { get; set; } int Add(Object value); bool Contains(Object value); void Clear(); bool IsReadOnly { get; } bool IsFixedSize { get; } int IndexOf(Object value); void Insert(int index, Object value); void Remove(Object value); void RemoveAt(int index); }
The code snippet below shows how you can create an instance of type IList and add and remove elements to and from the collection.
IList<string> customers = new List<string>(); customers.Add("Joydip"); customers.Add("Steve"); customers.Add("Peter"); customers.Insert(2, "Michael"); customers.RemoveAt(0); for (int i = 0; i < customers.Count; i++) { Console.WriteLine(customers[i]); }
Note how a customer name has been inserted to the collection and the customer name at the beginning of the collection has been deleted. When you execute the preceding piece of code, the names present in the updated collection will be displayed at the console window as shown in Figure 1.
The IQueryable interface in C#
The IQueryable interface pertaining to the System.Linq namespace extends the IEnumerable interface and can be used to query data from data sources that implement IQueryable providers. The IQueryable<T>interface extends the IEnumerable<T> interface and is defined as shown in the code snippet given below.
public interface IQueryable<out T> : IEnumerable<T>, IQueryable { }
Whereas the IEnumerable<T> interface can be used for working with in-memory collections of data, the IQueryable<T> interface can be used with external data sources such as a web service or a database. Consider the following class.
public class Author { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public bool IsActive { get; set; } }
The following code snippet demonstrates how you can work with the IQueryable interface in C#.
List<Author> authors = new List<Author>() { new Author(){Id = 1, FirstName = "Joydip", LastName = "Kanjilal", IsActive = true}, new Author(){Id = 2, FirstName = "Michael", LastName = "Smith", IsActive = false}, new Author(){Id = 3, FirstName = "Steve", LastName = "Jones", IsActive = true} }; IQueryable<Author> query = authors.AsQueryable() .Where(a => a.IsActive == true); foreach (var author in query) { Console.WriteLine($"Id : {author.Id} FirstName : {author.FirstName} LastName : {author.LastName}"); }
The IQueryable interface is appropriate for working with large datasets, particularly when you need to implement paging to retrieve only the data you need.
Which collection interface should you use?
Here’s a quick recap of the features of the IEnumerable, ICollection, IList, and IQueryable interfaces.
- IEnumerable allows you to access elements of a collection in a read-only, forward-only manner. No adding, deleting, or modifying the elements.
- ICollection inherits from the IEnumerable interface and provides additional functionality including adding, deleting, and modifying elements.
- IList extends the ICollection interface and represents strongly typed collections that are accessible via index. So in addition to adding, deleting, and modifying, you can insert or remove items using index values.
- IQueryable extends the IEnumerable interface, provides support for LINQ, and is well-suited for working with large datasets. When you use IQueryable with LINQ to SQL or LINQ to Entities, it generates a LINQ to SQL expression that is executed in the data layer of your application.
The choice of collection interface in C# will depend on the requirements of your application. If you want to query data from a database, use IQueryable. If you want to query data from memory, then use IEnumerable, ICollection, or IList, depending on what you want to do with the elements of the collection.