foreach and type safety

Johan Kullbom [HiddenEMail]
2008 (last updated 2008-07-22)

Valid XHTML 1.0 Transitional
Valid CSS!

foreach is probably the most common language construct for iteration in C# and because of this is it worth knowning that there is a serious problem in using it. When used with enumerations over reference types foreach (just like arrays) lacks any notation of type safety.

According to the C# 2.0 standard (ECMA-334 C# Language Specification under “15.8.4 The foreach statement” p.238):

[…] a collection type C, enumerator type E and elementtype T. A foreach statement of the form

foreach (V v in x) embedded-statement

is then expanded to:

{
  E e = ((C)(x)).GetEnumerator();
  try {
    V v;
    while (e.MoveNext()) {
      v = (V)(T)e.Current;
      embedded-statement
    }
  }
  finally {
    ... // Dispose e
  }
}

Exactly what types that is actually expanded as C, E and T and which code "Dispose e" represents is a result of a fairly advanced (or at least complicated - think of a large chunk of nested ifs) algorithm of cases based on whether x implements IEnumerable, IEnumerable<T>, is an array type and so on. (The rules are described in detail in ECMA-334.)

For programmers interested in type safety and fundamental design the generic IEnumerable interface is probably the most common type to use together with foreach:

IEnumerable<Animal> myCollection = ...
foreach(Animal myElement in myCollection)
  doSometing(myElement);

Which the compiler will expanded to:

IEnumerable<Animal> myCollection = ...
{
  IEnumerator<Animal> e = ((IEnumerable<Animal>)(myCollection)).GetEnumerator();
  try {
    Animal myElement;
    while (e.MoveNext()) {
      myElement = (Animal)(Animal)e.Current;
      doSometing(myElement);
    }
  }
  finally {
    System.IDisposable d = e as System.IDisposable;
    if (d != null) d.Dispose();
  }
}

This expansion is not in itself wrong even though there are a few casts in there that is not needed. It get worse if we, by mistake, write a subtype in place of Animal as the element type:

IEnumerable<Animal> myCollection = ...
foreach(Giraffe myElement in myCollection)
  doSometing(myElement);

Since the compiler accept upcasting of an Animal to a Giraffe it will expand this without any complaint to:

IEnumerable<Animal> myCollection = ...
{
  IEnumerator<Animal> e = ((IEnumerable<Animal>)(myCollection)).GetEnumerator();
  try {
    Giraffe myElement;
    while (e.MoveNext()) {
      myElement = (Giraffe)(Animal)e.Current; // <-- Note!
      doSometing(myElement);
    }
  }
  finally {
    System.IDisposable d = e as System.IDisposable;
    if (d != null) d.Dispose();
  }
}

We will then get a InvalidCastException (from Animal to Giraffe) at runtime!

To be such a central construct in the language as foreach is I think it is an unbelievable serious flaw to open up for type errors like this.

Generic lists (List<T>) has a ForEach-method which does the same thing as foreach with the difference of being type safe. This method could have been moved to be a part of IEnumerable<T> when LINQ where introduced and the problem would have been solved but this was not done.

The only sollution left if the write the code yourself and do your best to forget that foreach ever existed:

public static void ForEach<T>(this IEnumerable<T> sequence, Action<T> action) {
  IEnumerator<T> e = sequence.GetEnumerator();
  try {
    while (e.MoveNext())
      action(e.Current);
  }
  finally {
    System.IDisposable d = e as System.IDisposable;
    if (d != null) d.Dispose();
  }
}