С# Nuances: foreach

You may have been asked a question on a job interview: “What needs to be done to make your class work with the foreach loop?” Well, the answer is “To implement IEnumerable”. The answer is correct but not complete.

In very deed, foreach uses the Duck typing. Therefore, it is enough to have the GetEnumerator method that returns “something” that has the MoveNext method and the Current property. It is not necessarily to remember these methods. If you pass an incorrect class into the foreach loop, the compiler will let you know what is missing.

Here is an example

Incorrect container

The compiler error will be as follow: foreach statement cannot operate on variables of type ‘Container’ because ‘Container’ does not contain a public definition for ‘GetEnumerator’

Let’s add the GetEnumerator method to the container, and also add Enumerator.

Correct container

Incorrect Enumerator

Now the compiler error will be as follow: foreach requires that the return type ‘Enumerator’ of ‘Container.GetEnumerator()’ must have a suitable public MoveNext method and public Current property.

Let’s add the MoveNext method and the Current property into Enumerator.

Correct Enumerator

Now compiler is OK! The Current property can return any type, either the ref type or the value type. Actually, this was the reason for using the Duck typing, in the times when there were no generics, to avoid unnecessary boxing and unboxing.

Another question that you may face is a question about IDisposable. In addition to the common questions about manual resource management, there may be a question about in which cases the compiler can automatically call the Dispose method. Hopefully, we all know the correct answer. Dispose is called automatically when you use the using statement (). However, this is not the whole story. The Dispose method can also be called inside the foreach loop for Enumerator, in case the Enumerator implements IDisposable.

Enumerator with Dispose

We will see the Dispose line in the console.

For this particular case, the compiler generates the following code

We will see the Dispose line in the console.

For this particular case, the compiler generates the following code

Another nuance is that old compilers would generate the following code.

The loop variable is declared in another place.

Andrey Langovoy

Andrey Langovoy

Andrey Langovoy is a team leader at Devart. He takes part in development and testing database management tools for SQL Server, writes articles about SQL Server and contributes to open source projects, MSDN and MDN.
Andrey Langovoy

Andrey Langovoy

Andrey Langovoy is a team leader at Devart. He takes part in development and testing database management tools for SQL Server, writes articles about SQL Server and contributes to open source projects, MSDN and MDN.