web-dev-qa-db-fra.com

C # pour chaque comportement inattendu

Pourquoi le compilateur C # permet-il de compiler et lève une exception d'exécution lors de l'exécution?

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

      foreach(IDisposable item in list)
      {

      }
   }
}

public class Test
{

}

Cela se compile avec n'importe quelle interface et ne se compile pas si vous remplacez IDisposable par une classe concrète.

8
ekalchev

La boucle foreach a un transtypage implicite. C'est à peu près comme ça:

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

Avant les génériques, c'était beaucoup plus pratique que de devoir transtyper le code source. Maintenant, ce n'est pas si important, mais il serait étrange qu'il ne compile pas. Notez que le compilateur va vérifier qu'il est au moins faisable. Si vous utilisez foreach (string item in list) qui ne compilerait pas, car un Test ne peut pas être un string - mais un Testcan être un IDisposable, car il pourrait faire référence à une instance d'une sous-classe de Test qui implémente IDisposable. Si vous rendez la classe Test scellée, elle ne pourra pas être compilée même avec IDisposable, car une instance Test ne pourra pas implémenter IDisposable.

Fondamentalement, il se compilera si un transtypage de Test vers le type d'itération se compilait, et échouera à la compilation sinon. Mais il échouera au moment de l'exécution si une conversion normale échouait également au moment de l'exécution.

16
Jon Skeet