C # para cada comportamiento inesperado

8

¿Por qué el compilador de C # permite que esto se compile y genera una excepción de tiempo de ejecución cuando se ejecuta?

class Program
{
   static void Main(string[] args)
   {
      IEnumerable<Test> list = new List<Test>() { new Test() };

      foreach(IDisposable item in list)
      {

      }
   }
}

public class Test
{

}

Esto compila con cualquier interfaz y no compila si reemplaza IDisposable con clase concreta.

ekalchev
fuente
44
Se compila porque no pueden existir casos que se derivan de Testlos cuales no aplicar IDisposable. Efectivamente, el foreach intenta lanzar cada elemento a la interfaz y lanza una excepción si esto falla. Lo mismo que si escribieras (IDisposable)currentElement. Sin embargo, no puedo ver por qué no debería compilarse en la clase concreta.
HimBromBeere
Según mi violín que se compila muy bien: dotnetfiddle.net/3Up9nU
HimBromBeere
3
@HimBromBeere: Sí, ese es el punto de la pregunta.
Jon Skeet
usted puede asegurarse de que no se quedará en una excepción mediante el uso foreach (IDisposable item in list.OfType<IDisposable>())pero supongo que no es el punto
Innat3

Respuestas:

16

El foreachbucle tiene un reparto implícito. Es más o menos así:

using (IEnumerator<Test> iterator = list.GetEnumerator())
{
    while (iterator.MoveNext())
    {
        IDisposable item = (IDisposable) iterator.Current;
        // Body of foreach loop here
    }
}

Antes de los genéricos, eso era mucho más práctico que tener que enviar el código fuente. Ahora no es tan importante, pero sería extraño que no se compilara. Tenga en cuenta que el compilador se compruebe que es al menos posible . Si usa foreach (string item in list)eso, no se compilaría, porque a Testno puede ser a string, pero a Test puede ser un IDisposable, porque podría referirse a una instancia de una subclase de Testesos implementos IDisposable. Si se Testsella la clase, no se compilará incluso con ella IDisposable, porque entonces una Testinstancia no se puede implementar IDisposable.

Básicamente, se compilará si se compila una conversión del Testtipo de iteración, y de lo contrario no se compilará. Pero fallará en el tiempo de ejecución si un lanzamiento normal también falla en el tiempo de ejecución.

Jon Skeet
fuente