2015-02-05 5 views
-1

У меня есть два вопроса:Должен ли я действительно использовать IEnumerable? Что такое System.Collections.IEnumerator?

Первый вопрос:

Я написал сканер, который генерирует поток токенов по требованию с помощью синтаксического анализа.

public class Scanner 
{ 
    public IEnumerator<Token> GetEnumerator() 
    { //DO SOME STUFF HERE 
     yield return new Token(...); 
    } 
} 

public class Parser 
{ 
    IEnumerator<Token> iterator; 
    ... 
    public Parser(Scanner scanner) 
    { 
     this.iterator = scanner.GetEnumerator(); 
     ... 
    } 
    public Token nextToken() 
    { 
     //generate the next token 
     iterator.MoveNext();//there is no need to test MoveNext() 
     return iterator.Current;//get the generated token 
    } 
    ... 
} 

Учитывая, что это на самом деле единственный случай, когда я использую класс сканера в целом рамки, как вы думаете, что это нормально или было бы более правильным, что Scanner реализует IEnumerable?

Если я не ошибаюсь, компилятор выполнит некоторую операцию, чтобы сделать сканер IEnumerable в любом случае, это правильно?

Второй вопрос:

Пусть Scanner реализует IEnumerable, то код будет выглядеть следующим образом:

public class Scanner:IEnumerable<Token> 
{ 
public IEnumerator<Token> GetEnumerator() 
{ 
    ... 
} 

IEnumerator IEnumerable.GetEnumerator() 
{ 
    return this.GetEnumerator();//I'M NOT SURE OF THIS! 
} 

То, что я не понимаю, в чем смысл IEnumerable.GetEnumerator() и если моя реализация правильно или нет. Я знаю, что это необходимо, так как Scanner реализует IEnumerable<T>, а не IEnumerable (в противном случае GetEnumerator() было бы достаточно, но тогда на Current я должен был сделать неэффективное понижение от объекта к токену).

Может ли кто-нибудь его exaplain?

ответ

3

IEnumerable<T> распространяется IEnumerable, поэтому, если вы реализуете IEnumerable<T>, вы автоматически также реализуете IEnumerable.

IEnumerable существует по одной причине: потому что у C# 1.0 не было дженериков. Из-за этого нам теперь нужно реализовать IEnumerable все время, что просто раздражает.

Ваша реализация верна: при внедрении IEnumerable просто позвоните в родовую версию.

IEnumerator IEnumerable.GetEnumerator() 
{ 
    return this.GetEnumerator(); 
} 
+0

Спасибо за ваш ответ на второй вопрос! У меня для вас два вопроса: 1. как насчет первого вопроса? 2. Есть какая-то особая причина, потому что я должен реализовать 'IEnumerable.GetEnumerator()' по-другому? – justHelloWorld

+0

IEnumerable - это не просто наследие. Он позволяет перечислять объекты. Это не общий, а обобщенный способ перечисления. – TIKSN

+0

@TIKSN довольно много наследия. 'IEnumerable ' является предпочтительным, поскольку он подходит для LINQ. –

2

Цель IEnumerable<T> в отличие от IEnumerator<T> будет представлять собой последовательность значений, которые вы еще не начали перечислять еще. Вы можете хранить и передавать его, как контейнер значений, и когда вы хотите знать, что он содержит, вы можете начать перечисление его, вызвав GetEnumerator.

Или, как правило, вы должны использовать цикл foreach или LINQ, например Select, и Where для его прокрутки. Эти методы работают на IEnumerable<T>, а не на IEnumerator<T>.

Если вы используете только IEnumerator<T>, то перечисление может выполняться только один раз, в направлении вперед. Чтобы повторить перечисление, пользователь снова должен вызвать ваш метод, чтобы создать другой счетчик.

Таким образом, IEnumerable<T> является экземпляром «заводской картины». Это завод, который производит IEnumerator<T>.

(Что касается странных деталей о необщем IEnumerable, это только древняя история, как @Dennis_E говорит. Ваша реализация, которая направляет к общей версии совершенно стандартная.)

Как говорит @DmitryBychenko в комментариях, IEnumerator<T> основан на IDisposable. Цикл foreach заботится о вызове Dispose. И если последовательность в конечном счете реализована с использованием yield return, Dispose имеет эффект вызова каких-либо блоков finally, которые еще не выполнены.

+0

Еще одна причина заключается в том, что 'IEnumerator ' '' IDisposable', поэтому нам нужно закрыть выделенные ресурсы (через 'using'), когда' IEnumerable 'скрывает выделение/освобождение ресурсов –

+1

@DmitryBychenko - * отлично * point - будет обновляться до укажите, что foreach вызывает 'Dispose'. –

Смежные вопросы