Ответ 1
Это тонкий и немного неудачный. Легкое обходное решение:
public IEnumerator<object> GetEnumerator ( )
{
return ((IEnumerable<object>)this.Values).GetEnumerator ( );
}
Я думал, что тип массива реализовал оба интерфейса IEnumerable, не так ли?
Правила:
- System.Array реализует IEnumerable "неявно" с помощью общедоступных методов.
- каждый тип массива T [] наследует от System.Array.
- каждый тип массива T [] реализует
IList<T>
,IEnumerable<T>
и т.д. - поэтому каждый тип массива T [] можно преобразовать в
IEnumerable<T>
Обратите внимание, что третий пункт был НЕ
- каждый тип массива T [] реализует
IList<T>
,IEnumerable<T>
и т.д. с общедоступными методами и свойствами, определенными в T [], которые неявно реализуют члены
И ты туда. Когда вы смотрите GetEnumerator, мы смотрим его на объект [] и не находим его, потому что объект [] реализует IEnumerable<object>
явно. Он конвертируется в IEnumerable<object>
, а конвертируемость не учитывается для поиска. (Вы бы не ожидали, что метод "double" появится в int только потому, что int преобразуется в double.) Затем мы рассмотрим базовый тип и обнаружим, что System.Array реализует IEnumerable с помощью общедоступного метода, поэтому мы наш наш GetEnumerator.
То есть подумайте об этом так:
namespace System
{
abstract class Array : IEnumerable
{
public IEnumerator GetEnumerator() { ... }
...
}
}
class object[] : System.Array, IList<object>, IEnumerable<object>
{
IEnumerator<object> IEnumerable<object>.GetEnumerator() { ... }
int IList<object>.Count { get { ... } }
...
}
Когда вы вызываете GetEnumerator по объекту [], мы не видим реализации, которая является явной реализацией интерфейса, поэтому переходим к базовому классу, который имеет один видимый.
Как все объекты [], int [], string [], SomeType [] генерируются "на лету"?
Магия!
Это не дженерики, не так ли?
Right. Массивы представляют собой очень специальные типы, и их запекают на глубоком уровне в систему типа CLR. Хотя они очень похожи на дженерики во многих отношениях.
Кажется, что это
class object [] : System.Array
- это то, что не может быть реализовано пользователем, правильно?
Правильно, это было просто для того, чтобы проиллюстрировать, как думать об этом.
Какой из них, по вашему мнению, лучше: лить
GetEnumerator()
вIEnumerable<object>
или просто использоватьforeach
иyield
?
Вопрос плохо сформирован. Вы не используете GetEnumerator для IEnumerable<object>
. Вы либо передаете массив в IEnumerable<object>
, либо добавили GetEnumerator в IEnumerator<object>
.
Я бы, вероятно, использовал значения IEnumerable<object>
и набрал GetEnumerator
на нем.
Я, вероятно, буду использовать кастинг, но мне интересно, если это место, где вы или какой-то программист, который мог бы читать код, подумали бы об этом менее ясно.
Я думаю, что это довольно ясно с актерским составом.
когда вы сказали неявную реализацию, вы имеете в виду в виде интерфейса. Метод, правильно?
Нет, наоборот:
interface IFoo { void One(); void Two(); }
class C : IFoo
{
public void One() {} // implicitly implements IFoo.One
void IFoo.Two() {} // explicitly implements IFoo.Two
}
Первая декларация беззвучно реализует метод. Во-вторых, явствует, какой интерфейс он реализует.
Какая причина для реализации
IEnumerable<T>
как это, вместо неявной реализации с использованием общедоступных методов? Мне стало любопытно, потому что вы сказали: "Это тонкий и немного неудачный", так кажется, что из-за более старого решения, которое заставило вас сделать это, я думаю?
Я не знаю, кто принял это решение. Однако это печально. Это смутило хотя бы одного пользователя - вас... и это тоже смутило меня на несколько минут!
Я бы подумал, что тип массива будет примерно таким:
public class Array<T> : IEnumerable<T>
и т.д. Но вместо этого в нем есть какой-то магический код, верно?
Right. Как вы отметили в своем вчерашнем вопросе, все было бы очень по-другому, если бы у нас были генерики в CLR v1.
Массивы по существу представляют собой общий тип коллекции. Поскольку они были созданы в системе типов, у которой не было дженериков, для обработки их должно быть много специального кода в системе типов.
В следующий раз, когда вы создадите систему типов, поместите generics в v1 и убедитесь, что вы получаете сильные типы коллекций, типы с нулевым значением и типы с нулевым значением, испеченные в структуре с самого начала. Добавление генерических и нулевых типов значений post hoc было затруднительным.