C # für jedes unerwartete Verhalten

8

Warum erlaubt der C # -Compiler das Kompilieren und löst beim Ausführen eine Laufzeitausnahme aus?

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

      foreach(IDisposable item in list)
      {

      }
   }
}

public class Test
{

}

Dies wird mit jeder Schnittstelle kompiliert und nicht kompiliert, wenn Sie IDisposable durch eine konkrete Klasse ersetzen.

ekalchev
quelle
4
Es wird kompiliert, weil möglicherweise Instanzen vorhanden sind, von Testdenen es implementiert wird IDisposable. Tatsächlich versucht foreach, jedes Element in die Schnittstelle umzuwandeln, und löst eine Ausnahme aus, wenn dies fehlschlägt. Das gleiche, als würdest du schreiben (IDisposable)currentElement. Ich kann jedoch nicht verstehen, warum es nicht in der konkreten Klasse kompiliert werden sollte.
HimBromBeere
Nach meiner Geige, die gut kompiliert: dotnetfiddle.net/3Up9nU
HimBromBeere
3
@HimBromBeere: Ja, das ist der Punkt der Frage.
Jon Skeet
Sie könnten sicherstellen, dass Sie nicht auf eine Ausnahme foreach (IDisposable item in list.OfType<IDisposable>())
stoßen,

Antworten:

16

Die foreachSchleife enthält eine implizite Besetzung. Es ist ungefähr so:

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

Vor den Generika war das viel einfacher, als den Quellcode eingeben zu müssen. Jetzt ist es nicht so wichtig, aber es wäre seltsam, wenn es nicht kompiliert würde. Beachten Sie, dass der Compiler wird prüfen, ob es zumindest ist machbar . Wenn Sie das verwenden foreach (string item in list), wird es nicht kompiliert, weil a Testnicht a sein kann string- aber a Test kann a sein IDisposable, weil es auf eine Instanz einer Unterklasse Testdieser Implementierungen verweisen könnte IDisposable. Wenn Sie die TestKlasse versiegeln, kann sie auch mit nicht kompiliert IDisposablewerden, da eine TestInstanz dann nicht implementiert werden kann IDisposable.

Grundsätzlich wird es kompiliert, wenn eine Umwandlung von Testin den Iterationstyp kompiliert werden würde, und es wird sonst nicht kompiliert. Aber es wird zur Ausführungszeit fehlschlagen, wenn eine normale Besetzung auch zur Ausführungszeit fehlschlagen würde.

Jon Skeet
quelle