Introduction
The IEnumerable interface is the base Interface for many collections in C#, and its job is to provide a way of iteration through a collection. That is why we can use for each loops to go through a List or a Dictionary. In simple English when a collection class implements the IEnumerable interface it becomes countable, and we can count each element in it individually
There are 2 versions of the IEnumerable Interface
1.IEnumerable <T> for generic collections
2.IEnumerable for nongeneric collections
But since generic collections were introduced later after the non-generic ones and it is no longer recommended to use the non-generic collections in a new project due to their need to perform boxing and unboxing (converting the types of objects), we will explain how to use the
IEnumerable <T> interface in this lesson
IEnumerable<T> contains a single method that you must implement when implementing this interface; GetEnumerator(), which returns an IEnumerator<T> object. The returned IEnumerator<T> provides the ability to iterate through the collection by exposing a Current property that points at the object we are currently at in the collection.
IEnumerables have a method to get the next item in the collection, so they look at items “one at a time,” they don’t need the whole collection to be in memory. They don’t know how many items are in it for each loop. It just keeps getting the next item until it runs out. IEnumerables also help ensure immutability (read-only)
when it is recommended to use the IEnumerable interface:
- Your collection represents a massive database table, you don’t want to copy the entire thing into memory and cause performance issues in your application.
When it is not recommended to use the IEnumerable interface:
- You need the results right away and are possibly mutating/editing the objects later on. In this case, it is better to use an Array or a List
Making your Classes Enumerable
In this example, we will see how to make our class Enumerable meaning we can use an instance of it inside a for each loop
let’s consider the following classes
Class 1: a class called dog which has two properties
- string Name
- bool IsNaughtyDog
and one method
- GiveTreat() which will cause the dog to say “wuf” on the console :)
//a class named Dog which we will use in another class called DogShelter which will contain a collection of this class
class Dog {
//the name of the dog
public string Name { get; set; }
//is this a naughty dog
public bool IsNaughtyDog { get; set; }
//simple constructor
public Dog(string name,bool isNaughtyDog) {
this.Name = name;
this.IsNaughtyDog = isNaughtyDog;
}
//this method will print how many treats the dog received
public void GiveTreat(int numberofTreats) {
//print a message containing the number of treats and the name of the dog
Console.WriteLine("Dog: {0} said wuoff {1} times!.", Name, numberofTreats);
}
}
Class 2: a class called DogShelter
Which contains the List of dogs and that’s it
The List of dogs is initialized in the constructor
//a class named DogShelter this class contains a generic collection of type Dog
//objects of this class can't be used inside a for each loop because it lacks an implementation of the IEnumerable interface
class DogShelter {
//list of type List<Dog>
public List<Dog> dogs;
//this constructor will initialize the dogs list with some values
public DogShelter() {
//initialize the dogs list using the collection-initializer
dogs = new List<Dog>() {
new Dog("Casper",false),
new Dog("Sif",true),
new Dog("Oreo",false),
new Dog("Pixel",false),
};
}
}
Now in the main method let’s create a new DogShelter object and try to Enumerate through it
using a for each loop
using System;
using System.Collections.Generic;
namespace IEnumerableandIEnumerator {
class Program {
static void Main(string[] args) {
//create a new object of type DogShelter
DogShelter shelter = new DogShelter();
//for each dog in the DogShelter object
foreach(Dog dog in shelter) {//error because the DogShelter class does't implement the IEnumerable interface yet
//if the dog is not naughty (good boy)
if (!dog.IsNaughtyDog) {
//give this dog 2 treats
dog.GiveTreat(2);
} else {
//also give treats :) but only 1
dog.GiveTreat(1);
}
}
}
}
}
We will Immediately see an error , This error is saying that our class lacks the ability to be Enumerated through
To fix this we need to implement the interface IEnumerbale<T> where T is going to be the type of objects that we will enumerate through which in our case is Dog
first, since this interface is concerned with the generic collections, we need to import the generic collections namespace
after we implement the interface IEnumerable<Dog> our class should look like this
class DogShelter: IEnumerable<Dog> {// error because this class doesn't provide it's implementation of the methods from the IEnumerable interface
//list of type List<Dog>
public List<Dog> dogs;
//this constructor will initialize the dogs list with some values
public DogShelter() {
//initialize the dogs list using the collection-initializer
dogs = new List<Dog>() {
new Dog("Casper",false),
new Dog("Sif",true),
new Dog("Oreo",false),
new Dog("Pixel",false),
};
}
}
Now the error inside the for each loop is gone, but another error has appeared on the IEnumerable<Dog>
It is complaining because we did not implement the two methods that are inside the IEnumerable Interface, which are
- IEnumerable<Dog>.GetEnumerator() which is the method we need to implement to make our class Enumerable.
- IEnumerable.GetEnumerator() which is the non-generic version of the first method
Now the reason we need to implement the second method (non-generic version) is to ensure backward compatibility with the non-generic collections just in case because the generic collections came after the non-generic collections
Let’s hit show potential fixes
and then select Implement Interface
These will the two methods we mentioned above in our class
The class should look like this afterward
class DogShelter : IEnumerable<Dog> {
//list of type List<Dog>
public List<Dog> dogs;
//this constructor will initialize the dogs list with some values
public DogShelter() {
//initialize the dogs list using the collection-initializer
dogs = new List<Dog>() {
new Dog("Casper",false),
new Dog("Sif",true),
new Dog("Oreo",false),
new Dog("Pixel",false),
};
}
//this method will get generated for us
//we will use this method to provide implementation to the GetEnumerator() method of the IEnumerator interface
public IEnumerator<Dog> GetEnumerator() {
throw new NotImplementedException();
}
//this method will get generated for us
//since in this example we won't be passing our class to a non-generic method we don't need to provide an implementation for it at this point
IEnumerator IEnumerable.GetEnumerator() {
throw new NotImplementedException();
}
}
Now since generic collections implement the IEnumerable Interface, then they also have their own implementation of the GetEnumerator() method, Which means in our case since the DogShelter uses a List<Dog> as a collection then we can use the GetEnumerator() of that list as a workaround
Our DogShelter class should then look like this
class DogShelter : IEnumerable<Dog> {
//list of type List<Dog>
public List<Dog> dogs;
//this constructor will initialize the dogs list with some values
public DogShelter() {
//initialize the dogs list using the collection-initializer
dogs = new List<Dog>() {
new Dog("Casper",false),
new Dog("Sif",true),
new Dog("Oreo",false),
new Dog("Pixel",false),
};
}
//this method will get generated for us
//we will use this method to provide implementation to the GetEnumerator() method of the IEnumerator interface
public IEnumerator<Dog> GetEnumerator() {
//since our list of dogs is a generic collection that already implements its own IEnumerable interface
//we will return it
return dogs.GetEnumerator();
}
//this method will get generated for us
//since in this example we won't be passing our class to a non-generic method we don't need to provide an implementation for it at this point
IEnumerator IEnumerable.GetEnumerator() {
throw new NotImplementedException();
}
}
Complete Code
using System;
using System.Collections.Generic;
namespace IEnumerableandIEnumerator {
class Program {
static void Main(string[] args) {
//create a new object of type DogShelter
DogShelter shelter = new DogShelter();
//for each dog in the DogShelter object
foreach(Dog dog in shelter) {//error because the DogShelter class does't implement the IEnumerable interface yet
//if the dog is not naughty (good boy)
if (!dog.IsNaughtyDog) {
//give this dog 2 treats
dog.GiveTreat(2);
} else {
//also give treats :) but only 1
dog.GiveTreat(1);
}
}
}
}
//a class named Dog which we will use in another class called DogShelter which will contain a collection of this class
class Dog {
//the name of the dog
public string Name { get; set; }
//is this a naughty dog
public bool IsNaughtyDog { get; set; }
//simple constructor
public Dog(string name,bool isNaughtyDog) {
this.Name = name;
this.IsNaughtyDog = isNaughtyDog;
}
//this method will print how many treats the dog received
public void GiveTreat(int numberofTreats) {
//print a message containing the number of treats and the name of the dog
Console.WriteLine("Dog: {0} said wuoff {1} times!.", Name, numberofTreats);
}
}
class DogShelter : IEnumerable<Dog> {
//list of type List<Dog>
public List<Dog> dogs;
//this constructor will initialize the dogs list with some values
public DogShelter() {
//initialize the dogs list using the collection-initializer
dogs = new List<Dog>() {
new Dog("Casper",false),
new Dog("Sif",true),
new Dog("Oreo",false),
new Dog("Pixel",false),
};
}
//this method will get generated for us
//we will use this method to provide implementation to the GetEnumerator() method of the IEnumerator interface
public IEnumerator<Dog> GetEnumerator() {
//since our list of dogs is a generic collection that already implements its own IEnumerable interface
//we will return it
return dogs.GetEnumerator();
}
//this method will get generated for us
//since in this example we won't be passing our class to a non-generic method we don't need to provide an implementation for it at this point
IEnumerator IEnumerable.GetEnumerator() {
throw new NotImplementedException();
}
}
}