Difference Between .NET Collection Interfaces

If you’re like some of the new .NET developers you’ve probably seen IEnumerable<T>, IList<T>and perhaps ICollection<T> around. If you’re anything like most people, you’ve probably confused these interfaces and are unsure of which one to use when.

These interfaces are everywhere. For example, if you want to see List<T> In modern .NET implementations of the class you will see that it implements (among others) the following interfaces:

  • IList<T>
  • ICollection<T>
  • IReadOnlyList<T>
  • IReadOnlyCollection<T>
  • IEnumerable<T>

Each of these interfaces has a point, and each is slightly different. However, many of these interfaces can be intimidating when you first encounter them.

In this article we’ll explore these common .NET interfaces, what they provide, and when you can choose to use each.

Note: IEnumerable and IList also have older non-generic interfaces. For the purposes of this article we will focus on the common collection interfaces as they are more commonly used in modern .NET.

list <टी> Why not use

when I teach my students about IEnumerable, I usually see the eye gleam from the abstraction of the concept and the exoticism of the countable word. A minor part of my students start to panic and wonder if they can just fear for the safety of working with List<T> And pretend there aren’t interfaces.

The short answer is: you can do ignore collection interface some of time

However, sometimes you have to work with these interfaces when others provide them to you, and sometimes it is better to work with these interfaces rather than concrete types.

To find out, let’s look at this simple example below:

private List<User> _users = new();
public List<User> Users
      return _users;
enter fullscreen mode

exit fullscreen mode

Here it looks like we have a properly encapsulated list of user objects _usersFarm. However, upon closer examination, there are some problems:

Firstly, a. returningList<User> We list the result of this call <उपयोगकर्ता> Invoke others to be stored in variables or properties. That’s fine for now, but if we ever change the type of the collection we’re returning, our callers will need to update as well.

second, a returningList<User>We give the calling methods full access to the methods on that List object, including Add, RemoveAnd Sort, Even worse, these methods will work and update the same ListOur code is using in its _users field.

This violates encapsulation and may lead to inconsistent state within the class due to unexpected way changes in the internal state of the class coming from outside the class.

Alternatively, if it was not actually modify the class, but the caller still thought they could use Add, Removeeither Sortways, they are now confused and frustrated because our design did not indicate to them that they could not do Do it.

While these problems are usually not catastrophic, it is better to avoid them if we can, which is where .NET collection interfaces come into play.

Common .NET Collection Interfaces

Since a single diagram can be far more efficient than entire sections, I’ve summarized these five interfaces in this comparison matrix:

In the sections below we’ll explore each of these five interfaces and when to use each.


IEnumerable<T> .NET has a basic interface which simply means that something can be looped in a foreachStatement.

IEnumerable<T> is supported by List<T>Arrays, and almost any other collection type in .NET.

That’s all you need to know about IEnumerable<T>But if you want some more details, read on to know about it IEnumerator<T> Too.

IEnumerable<T> internally a. works by providing GetEnumerator method that returns a IEnumerator<T> Thing.

This IEnumerator<T> Provides methods of:

  • Get current item (if one exists)
  • go to next item
  • reset back to first item

These three capabilities power any collection type that can be looped.


ICollection<T> Allows you to manage collections of objects. all ICollection<T>s are also IEnumerable<T>,

using the ICollection<T> you can:

  • Received CountAbout the number of items in the collection
  • see if a collection Containssome
  • modify the collection by Add, RemoveAnd Clearmethods
  • copy a collection into an array
  • Determine if collection is read only

especially, ICollection<T> The interface does not include array indexes, so you cannot use index to remove an item from a collection by its index.

ICollection<T> This is a great option if you need to add and remove things from a collection as well as loop over it, but you don’t need to use indexes.


IList<T> .NET has an extremely powerful and generic ordered collection interface.

IList<T>s all implement both ICollection<T> And IEnumerable<T> But add the ability to work with indexes.

especially, IList<T> Adds the following capabilities:

  • collection indexer which lets you use syntax like int number = myList[0],
  • allows you to find IndexOfan item in a list
  • Insertitems at a specific index
  • RemoveAta specific index

If your collection is ordered and has specific indexes and you want others to take full advantage of them, IList<T> is a great option.


IReadOnlyCollection<T> A rare interface to see, but it’s essentially a version of IEnumerable<T> Including the count of objects.

you can also think IReadOnlyCollection<T> Collection As a variant of that which removes methods to modify objects.

all IReadOnlyCollection<T>s are also IEnumerable<T>,

IReadOnlyCollection<T> This is a good option if you want to return a collection but don’t want to expose methods to manipulate it via an interface.


IReadOnlyList<T> is an extension of IReadOnlyCollection<T> But adds an indexer so that you can get values ​​from a collection by index. Otherwise, IReadOnlyList<T> functionally identical IReadOnlyCollection<T>,

protection against casting

Now you must be thinking that IReadOnlyList or IReadOnlyCollection Using it keeps you safe from others modifying your code.

However, this may not actually be entirely true.

Let’s revisit our example from earlier, but IReadOnlyList take advantage of:

private List<User> _users = new();
public IReadOnlyList<User> Users
      return _users;
enter fullscreen mode

exit fullscreen mode

Here a complete list of class internals<उपयोगकर्ता> but only for external callers IReadOnlyList can expose.

This prevents callers from doing the following:

// Wouldn't work since Users is an IReadOnlyList
myObject.Users.Add(new User("Matt Eland"));
enter fullscreen mode

exit fullscreen mode

However, since the actual object being returned above is still a List<User>Knowledgeable callers can notice this and get the result as a . can put on List<User> and then interact with it in a way you didn’t intend:

// Cast the result of the Users property to a List<User>
List<User> users = (List<User>)myObject.Users;

// This works
users. Add(new User("Matt Eland"));
enter fullscreen mode

exit fullscreen mode

Because of this, it’s a good idea to secure the objects you only want to read by calling ToList() either AsReadOnly() On them using LINQ to create a separate collection while returning the values ​​as shown below:

private List<User> _users = new();
public IReadOnlyList<User> Users
      return _users.AsReadOnly();
enter fullscreen mode

exit fullscreen mode


While it’s certainly possible to write code that just returns lists or other concrete types, using the appropriate .NET Collections interface can help your code shine.

Using the right interface helps others see your intent, helps you properly encapsulate your classes, and lets you change the collection type in a class without affecting callers.

Leave a Comment