Lets assume that you are want to hold and manage a sequence of elements in a typical c# application. You will use any of the data structures found in the System.Collection namespace to store and retrieve elements as per the requirements and specification.
To traverse through the elements in any collection, you will generally use the foreach looping structure provided in the language. The looping variable assigned will contain the current element of the collection being iterated over.
IEnumerator, IEnumerable and IQueryable are three interfaces used in storing, retrieving and querying over a collection of data.
In this article we will explain about each of these interfaces in detail and as well summarize them
What is an IEnumerator Interface?
IEnumerator according to microsoft is an interface from Systems.Collection namespace which provides the infrastructure to allow the caller to iterate over the internals of the implementing type.
It supports a simple iteration over a non-generic collection.
IEnumerator interface has methods to implement – MoveNext() and Reset().
The IEnumerator also provides a property Current which points to the current element that is touched during the iteration.
For example, if you have a variable of type List and you iterate over this collection variable as below:
IEnumerable<Mothers> items = new List<Mothers>();foreach (var item in items){ // code to access the item
}
The above code snippet is equivalent to the below –
IEnumerable<Mother> items = new List<Mother>(); var itemsEnumerator = items.GetEnumerator(); Mother item; while (itemsEnumerator.MoveNext()) { item = (Mother)itemsEnumerator.Current; // some code to access item of type Book }
We don’t generally access or implement the IEnumerator interface. The IEnumerator is wrapped by the IEnumerable interface, which provides any implementing collection with a suitable Enumerator to loop over.
We can use a collection in a foreach loop only if it implements an IEnumerable (which supplies the required IEnumerator instance)
The MoveNext() method moves only in a forward direction and not in reverse.
For cases where we may need to seek a previous element in the loop, or provide an alternative traversal strategy, we can consider implementing our own IEnumerator or using an Iterator design pattern.
What is an IEnumerable Interface?
The IEnumerable is an interface that represents a collection of elements that can be iterated over. It doesn’t contain any other methods or properties, except a single method GetEnumerator – that returns an IEnumerator object.
In the above example, we are using an IEnumerable of type Book to store a collection of objects and are able to iterate over it using the GetEnumerator method of the IEnumerable.
With an IEnumerable, you can only work with an in-memory collection of objects.
Any collection in the System.Collection namespace internally implement IEnumerable and can be casted into an IEnumerable when required.
An IEnumerable is Covariant – meaning you can assign any implementation of an IEnumerable to a variable of type IEnumerable in generics. In the above example, we are assigning an object of type List<Mother> to a variable of type IEnumerable<Mother>, where the type List implements IEnumerable.
Hence IEnumerable is considered Covariant on the parameters. You can find more detail about Covariance, Invariance and Contravariance in my detailed article here.
IEnumerable types support lazy evaluation or deferred execution – a concept where data is not fetched and put in memory until it is required and the IEnumerable is called. This helps with better memory management and application performance. You will need to use a yield keyword to implement this
You can also extend the functionality over an IEnumerable such as filtering, sorting or projection by importing the LINQ library and using the appropriate extension method over the IEnumerable collection.
What is an IQueryable?
IQueryable is an interface in the System.Linq namespace.
It is a special collection using which you can query and store data from an external source such as a database. It provides an IQueryProvider, which is used to create LINQ providers used in data-centric operations such as LINQ-to-SQL etc.
IQueryable collections use a concept called deferred or remote query execution, where the query operations on a collection are not executed immediately. Instead, they are translated into a query language such as SQL and are executed on a remote data source like a database server.
We can use the extension method AsQueryable() to convert an ienumerable to iqueryable collection. This enables us to apply deferred execution over this collection.
var students = new List<Student>();
students.Add(new Student { }); students.Add(new Student { }); students.Add(new Student { }); students.Add(new Student { });
// filtered is of type IQueryable var filtered = from r in students where r.Rank < 10 select r;
// evaluation takes place, result is printed foreach (var student in filtered) { Console.WriteLine("Student: {student.Name}, Rank: {student.Rank}"); }
students.Add(new Student { }); students.Add(new Student { });
// evaluation takes place again, result is printed foreach (var student in filtered) { Console.WriteLine("Student: {student.Name}, Rank: {student.Rank}"); }
On the other hand, you can use AsEnumerable() over an IQueryable collection to convert it into an IEnumerable collection.
Then a consolidated query that is the combination of all the conditions that have been applied over the IQueryable is generated and is executed to get a result on the external source.
AsEnumerable() or ToList() methods break deferred execution of an IQueryable.
Conclusion
IEnumerator provides a way to loop over elements in a collection. IEnumerable is a collection that provides an Enumerator to loop over elements. IQueryable provides querying capabilities over a collection from various sources with efficient performance.
Written by: Idika Destiny
Reading time:
Published 21 hours Ago On Friday, September 15, 2023
651 Views