C# tutorials > Core C# Fundamentals > Data Structures and Collections > What are the main interfaces in the .NET Collections Framework (`IEnumerable`, `ICollection`, `IList`, `ISet`, `IDictionary`)?
What are the main interfaces in the .NET Collections Framework (`IEnumerable`, `ICollection`, `IList`, `ISet`, `IDictionary`)?
The .NET Collections Framework provides a set of interfaces and classes that define and implement various data structures. Understanding the core interfaces is crucial for effectively working with collections in C#. This tutorial will explore the main interfaces: IEnumerable
, ICollection
, IList
, ISet
, and IDictionary
.
Introduction to .NET Collections Framework Interfaces
The .NET Collections Framework offers a variety of interfaces that provide a standardized way to work with groups of objects. Each interface defines a contract that classes can implement to provide specific collection behaviors. These interfaces build upon each other, offering increasing levels of functionality.
IEnumerable: The Foundation for Iteration
Any class that implements IEnumerable
is the most basic interface for collections in .NET. It defines a single method, GetEnumerator()
, which returns an IEnumerator
object. The IEnumerator
allows you to iterate over the elements in the collection using a foreach
loop.IEnumerable
can be iterated over. This interface establishes the fundamental concept of a sequence of elements.
public interface IEnumerable
{
IEnumerator GetEnumerator();
}
ICollection: Adding Size and Modification
ICollection
extends IEnumerable
and adds properties and methods for getting the size of the collection (Count
) and copying the collection to an array (CopyTo
). It also includes properties related to thread safety (IsSynchronized
and SyncRoot
), although these are less commonly used in modern .NET development.ICollection
represents a collection that can be modified (in some implementations). It provides basic information about the collection, such as its size.
public interface ICollection : IEnumerable
{
int Count { get; }
bool IsSynchronized { get; }
object SyncRoot { get; }
void CopyTo(Array array, int index);
}
IList: Indexed Access and Ordering
IList
extends ICollection
and adds the ability to access elements by their index. It introduces the indexer (this[int index]
), allowing you to get or set elements at specific positions. It also includes methods for adding, inserting, removing, and clearing elements.IList
represents an ordered collection where elements can be accessed by their position. Examples include arrays and lists.
public interface IList : ICollection
{
object this[int index] { get; set; }
int Add(object value);
void Clear();
bool Contains(object value);
int IndexOf(object value);
void Insert(int index, object value);
bool IsFixedSize { get; }
bool IsReadOnly { get; }
void Remove(object value);
void RemoveAt(int index);
}
ISet: Unique Values Only
Typical implementations include ISet
represents a collection of unique elements. It implements ICollection
, but adds methods to perform set operations such as union, intersection, and difference. The key feature of an ISet
is that it does not allow duplicate elements.HashSet<T>
and SortedSet<T>
.
public interface ISet<T> : ICollection<T>, IEnumerable<T>, IEnumerable
{
bool Add(T item);
void ExceptWith(IEnumerable<T> other);
void IntersectWith(IEnumerable<T> other);
bool IsProperSubsetOf(IEnumerable<T> other);
bool IsProperSupersetOf(IEnumerable<T> other);
bool IsSubsetOf(IEnumerable<T> other);
bool IsSupersetOf(IEnumerable<T> other);
bool Overlaps(IEnumerable<T> other);
bool SetEquals(IEnumerable<T> other);
void SymmetricExceptWith(IEnumerable<T> other);
void UnionWith(IEnumerable<T> other);
}
IDictionary: Key-Value Pairs
Examples include IDictionary
represents a collection of key-value pairs. It extends ICollection
and provides methods for adding, retrieving, and removing elements based on their key. The indexer (this[object key]
) allows you to access values by their associated key.Hashtable
and Dictionary<TKey, TValue>
.
public interface IDictionary : ICollection
{
object this[object key] { get; set; }
ICollection Keys { get; }
ICollection Values { get; }
void Add(object key, object value);
void Clear();
bool Contains(object key);
IDictionaryEnumerator GetEnumerator();
void Remove(object key);
}
Concepts Behind the Snippet
The core concept behind these interfaces is to provide a common way to work with different types of collections. By adhering to these interfaces, classes can be written that operate generically on any collection type, as long as it implements the required interface. This promotes code reusability and flexibility. The inheritance relationship between these interfaces (IEnumerable
-> ICollection
-> IList
, IEnumerable
-> ICollection
-> IDictionary
and IEnumerable
-> ICollection
-> ISet
) reflects the increasing levels of functionality they provide.
Real-Life Use Case Section
Consider a scenario where you need to store a list of students in a class. You could use an If you want to store student IDs as keys and student objects as values, you would use an IList<Student>
to store the students in a specific order and access them by their index. If you only need to iterate over the students without requiring indexed access, you could use an IEnumerable<Student>
.IDictionary<int, Student>
. If you only want to store unique student names, an ISet<string>
would be appropriate.
Best Practices
IEnumerable
instead of IList
.List<T>
, Dictionary<TKey, TValue>
) to avoid boxing and unboxing, improving performance and type safety.
Interview Tip
Be prepared to explain the differences between these interfaces and provide examples of when you would use each one. Understanding the underlying principles of the Collections Framework is a common interview question for C# developers.
When to Use Them
IEnumerable
: When you only need to iterate over a sequence of elements.ICollection
: When you need to know the size of a collection and copy it to an array, or potentially modify the collection.IList
: When you need indexed access to elements and the ability to add, insert, remove, and clear elements.ISet
: When you need a collection of unique elements and set operations.IDictionary
: When you need to store and retrieve data based on key-value pairs.
Memory Footprint
The memory footprint of each collection type depends on the underlying implementation and the data it stores. Generally, IEnumerable
itself has a minimal footprint, as it only defines the interface for iteration. IList
and IDictionary
can have a larger footprint due to the additional data structures required for indexed access and key-value storage, respectively. ISet
implementations also vary but are generally optimized for efficient membership testing and set operations.
Alternatives
Besides the standard implementations of these interfaces like List<T>
, Dictionary<T,V>
, and HashSet<T>
, there are other more specialized collection types. For example, ConcurrentDictionary<T,V>
for thread-safe key-value storage, ImmutableList<T>
for immutable lists, and various collection types in third-party libraries such as those offered by Reactive Extensions (Rx) or more specialized data structure libraries. The choice depends on the specific needs of your application.
Pros
Cons
FAQ
-
What is the difference between `ICollection` and `IEnumerable`?
IEnumerable
is the base interface for all collections and provides the ability to iterate over elements.ICollection
extendsIEnumerable
and adds properties and methods for getting the size of the collection and copying it to an array. -
When should I use `IList` instead of `ICollection`?
Use
IList
when you need indexed access to elements, the ability to add, insert, remove, and clear elements, and the order of elements is important. UseICollection
when you only need to know the size of the collection and copy it to an array or enumerate the collection. -
What is the primary purpose of `ISet`?
The primary purpose of
ISet
is to represent a collection of unique elements. It is useful when you need to ensure that no duplicate elements are present in the collection. -
How does `IDictionary` differ from `IList`?
IDictionary
stores elements as key-value pairs, whileIList
stores elements in a linear sequence with indexed access. You use keys to access values in anIDictionary
, while you use indices to access elements in anIList
.