web-dev-qa-db-ja.com

C#foreachの予期しない動作

なぜC#コンパイラはこれをコンパイルして実行時にランタイム例外をスローするのを許可するのですか?

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

      foreach(IDisposable item in list)
      {

      }
   }
}

public class Test
{

}

これは、任意のインターフェイスでコンパイルされ、IDisposableを具象クラスで置き換えてもコンパイルされません。

8
ekalchev

foreachループには暗黙のキャストがあります。おおよそ次のようになります。

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

ジェネリックの前に戻ると、ソースコードをキャストするよりもずっと便利でした。今はそれほど重要ではありませんが、コンパイルしないのは奇妙です。コンパイラwillが少なくともfeasibleであることを確認してください。 Teststringにすることはできないため、コンパイルできないforeach (string item in list)を使用すると、Testcan = IDisposableになります。これは、Testを実装するIDisposableのサブクラスのインスタンスを参照する可能性があるためです。 Testクラスをシールすると、IDisposableでもTestを実装できないため、IDisposableを使用してもコンパイルに失敗します。

基本的に、Testから反復型へのキャストがコンパイルされる場合はコンパイルされ、そうでない場合はコンパイルに失敗します。ただし、通常のキャストも実行時に失敗すると、実行時に失敗します。

16
Jon Skeet