Foreach может вызывать InvalidCastException?
Представьте следующий код:
class foreach_convert
{
public static void method2()
{
List<IComparable> x = new List<IComparable>();
x.Add(5);
foreach (string s in x)
{
//InvalidCastException in runtime
}
}
}
Интересно, почему это поведение foreach так... не похоже на С#?
Что происходит здесь, это неявный приведение к подклассу, который подвержен ошибкам и, кажется, запрещен в любом другом месте на этом языке. Или я не прав?
P.S. Причина, по которой я спрашиваю, заключается в том, что у меня была ошибка в аналогичном коде в моем проекте, где я использовал для итерации через пользовательскую коллекцию из внешней библиотеки, которая называлась как SomeTypeCollection
, но на самом деле предоставлялась коллекция базовых и могли содержать элементы SomeOtherType
. Моя ошибка, но все же ни язык, ни компилятор не предоставили никаких явных подсказок/предупреждений, что довольно необычно для С#...
Ответы
Ответ 1
Вспомните перед дженериками... foreach
должен был сделать так, чтобы вы могли делать такие вещи, как:
foreach (string x in names)
{
// ...
}
вместо:
foreach (object tmp in names)
{
string x = (string) tmp;
// ...
}
Последнее просто нехорошо, ИМО. Предоставление неявного литья в отличие от остальной части языка, но делает его намного проще в использовании в подавляющем большинстве случаев.
Я подозреваю, что если у С# были общие методы и методы расширения, чтобы начать (чтобы мы могли использовать OfType
и Cast
), что foreach
не будет указываться совершенно таким же образом.
Обратите внимание, что в foreach
еще больше нечетности: тип вообще не должен реализовывать IEnumerable
. До тех пор, пока у него есть метод GetEnumerator
, который возвращает что-то, что в свою очередь имеет MoveNext()
и Current
, компилятор С# счастлив. Это означало, что вы могли бы реализовать "строго типизированный итератор" (чтобы избежать бокса) до дженериков.
Ответ 2
С С# 3 я использую var - поэтому я получаю предупреждения компилятора.
Ответ 3
foreach работает над IEnumerable, который возвращает объекты типа object. Для каждого элемента объект будет передан в тип, который вы указали.
Если вы не используете var в С# 3.0. Тогда тип будет заимствован из IEnumerable<T>
, если он будет реализован в коллекции.