Я прочитал статью Eric здесь о перечислении foreach и о различных сценариях, в которых foreach может работать
Чтобы предотвратить старую версию С# для бокса, команда С# включила утиную печать для foreach для запуска в коллекции, отличной от Ienumerable. (Публичный GetEnumerator
, который возвращает то, что имеет общедоступные MoveNext
и Current
достаточно (.
Итак, Эрик написал образец:
class MyIntegers : IEnumerable
{
public class MyEnumerator : IEnumerator
{
private int index = 0;
object IEnumerator.Current { return this.Current; }
int Current { return index * index; }
public bool MoveNext()
{
if (index > 10) return false;
++index;
return true;
}
}
public MyEnumerator GetEnumerator() { return new MyEnumerator(); }
IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); }
}
Но я считаю, что у него есть некоторые опечатки (отсутствует get accessor при реализации свойства Current
), которые мешают ему компилировать (я уже отправил его по электронной почте).
В любом случае это рабочая версия:
class MyIntegers : IEnumerable
{
public class MyEnumerator : IEnumerator
{
private int index = 0;
public void Reset()
{
throw new NotImplementedException();
}
object IEnumerator.Current {
get { return this.Current; }
}
int Current {
get { return index*index; }
}
public bool MoveNext()
{
if (index > 10) return false;
++index;
return true;
}
}
public MyEnumerator GetEnumerator() { return new MyEnumerator(); }
IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); }
}
Ok.
Согласно MSDN:
Тип
C
называетсяcollection type
, если он реализуетSystem.Collections.IEnumerable
интерфейс или реализуетcollection pattern
, встретив все следующие критерии:
-
C содержит метод публичного экземпляра с подписями GetEnumerator(), который возвращает тип struct-type, class-type или interface-type, который называется E в следующем тексте.
-
E содержит метод общедоступного экземпляра с сигнатурой MoveNext() и возвращаемым типом bool.
-
E содержит свойство публичного экземпляра с именем Current, которое позволяет считывать текущее значение. Тип этого свойства называется типом элемента типа коллекции.
OK. Пусть сопоставление документов с образцом Эрика
Образец Eric называется collection type
, потому что он реализует интерфейс System.Collections.IEnumerable
(явно, хотя). Но это не (!) a collection pattern
из-за пули 3: MyEnumerator
не имеет свойства публичного экземпляра с именем Current.
MSDN говорит:
Если выражение коллекции имеет тип, который реализует (как определено выше), расширение foreach утверждение:
E enumerator = (collection).GetEnumerator();
try {
while (enumerator.MoveNext()) {
ElementType element = (ElementType)enumerator.Current;
statement;
}
}
finally {
IDisposable disposable = enumerator as System.IDisposable;
if (disposable != null) disposable.Dispose();
}
В противном случае, выражение коллекции имеет тип, который реализует System.IEnumerable(!), а расширение оператора foreach:
IEnumerator enumerator =
((System.Collections.IEnumerable)(collection)).GetEnumerator();
try {
while (enumerator.MoveNext()) {
ElementType element = (ElementType)enumerator.Current;
statement;
}
}
finally {
IDisposable disposable = enumerator as System.IDisposable;
if (disposable != null) disposable.Dispose();
}
Вопрос № 1
Кажется, что образец Эрика не реализует
collection pattern
и System.IEnumerable
- поэтому он не должен соответствовать ни одному из указанных выше условий. Итак, почему я все еще могу перебрать его через:
foreach (var element in (new MyIntegers() as IEnumerable ))
{
Console.WriteLine(element);
}
Вопрос № 2
Почему я должен упомянуть new MyIntegers() as IEnumerable
? он уже Ienumerable (!!) и даже после этого, разве компилятор уже выполняет эту работу самостоятельно посредством кастинга:
((System.Collections.IEnumerable)(collection)).GetEnumerator() ?
Это прямо здесь:
IEnumerator enumerator =
((System.Collections.IEnumerable)(collection)).GetEnumerator();
try {
while (enumerator.MoveNext()) {
...
Итак, почему он все еще хочет, чтобы я упоминал как Ienumerable?