Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

IEnumerable - Move Next

Tags:

c#

I have an IEnumerable from which I need to fetch each item and display it one by one. The displaying is not a continuous process..i.e I should fetch one item and display it on the UI, then wait for some user feedback on that item, and then move on to the next item. For example from the below code, I need to fetch a question, then display it to the user, then user hits enter, and then I move on to fetching the next question.

My Question is how do I do that? Is IEnumerable the best way of achieving this or should I revert to list and start storing the indexes and increment it one by one?

Please note that I'm using .NET 3.5.

Code:

class Program
    {
        static void Main(string[] args)
        {
            Exam exam1 = new Exam()
                             {
                                 Questions = new List<Question>
                                                 {
                                                     new Question("question1"),
                                                     new Question("question2"),
                                                     new Question("question3")
                                                 }
                             };
            var wizardStepService = new WizardStepService(exam1);
            var question = wizardStepService.GetNextQuestion();
            //Should output question1
            Console.WriteLine(question.Content);
            Console.ReadLine();
            //Should output question2 but outputs question1
            question = wizardStepService.GetNextQuestion();
            Console.WriteLine(question.Content);
            Console.ReadLine();
            //Should output question3 but outputs question1
            question = wizardStepService.GetNextQuestion();
            Console.WriteLine(question.Content);
            Console.ReadLine();
        }
    }

    public class Question
    {
        private readonly string _text;
        public Question(string text)
        {
            _text = text;
        }

        public string Content { get { return _text; } }
    }

    internal class Exam
    {
        public IEnumerable<Question> Questions { get; set; }
    }

    internal class WizardStepService
    {
        private readonly Exam _exam;
        public WizardStepService(Exam exam)
        {
            _exam = exam;
        }

        public Question GetNextQuestion()
        {
          foreach (var question in _exam.Questions)
            {              
              //This always returns the first item.How do I navigate to next 
              //item when GetNextQuestion is called the second time?
              return question;
            }

            //should have a return type hence this or else not required.
            return null;
        }
    }
like image 264
Mike Avatar asked Aug 14 '13 12:08

Mike


2 Answers

Yes, GetEnumerator() should work fine; although strictly speaking you need to handle Dispose():

internal class WizardStepService : IDisposable
{
    private IEnumerator<Question> _questions;
    public WizardStepService(Exam exam)
    {
        _questions = exam.Questions.GetEnumerator();
    }
    public void Dispose()
    {
        if (_questions != null) _questions.Dispose();
    }
    public Question GetNextQuestion()
    {
        if (_questions != null)
        {
            if (_questions.MoveNext())
            {
                return _questions.Current;
            }
            Dispose(); // no more questions!
        }        

        //should have a return type hence this or else not required.
        return null;
    }
}

and also:

using (var wizardStepService = new WizardStepService(exam1))
{
    var question = wizardStepService.GetNextQuestion();
    //Should output question1
    Console.WriteLine(question.Content);
    Console.ReadLine();
    //Should output question2 but outputs question1
    question = wizardStepService.GetNextQuestion();
    Console.WriteLine(question.Content);
    Console.ReadLine();
    //Should output question3 but outputs question1
    question = wizardStepService.GetNextQuestion();
    Console.WriteLine(question.Content);
    Console.ReadLine();
}

However, since you're not actually testing the result each time, you could also do something like:

using (var questions = exam1.Questions.GetEnumerator())
{
    questions.MoveNext();
    var question = questions.Current;
    //Should output question1
    Console.WriteLine(question.Content);
    Console.ReadLine();
    //Should output question2 but outputs question1
    questions.MoveNext();
    question = questions.Current;
    Console.WriteLine(question.Content);
    Console.ReadLine();
    //Should output question3 but outputs question1
    questions.MoveNext();
    question = questions.Current;
    Console.WriteLine(question.Content);
    Console.ReadLine();
}

Or as a final thought, perhaps just:

var questions = exam1.Questions.Take(3).ToArray();

//Should output question1
Console.WriteLine(questions[0].Content);
Console.ReadLine();
//Should output question2 but outputs question1
Console.WriteLine(questions[1].Content);
Console.ReadLine();
//Should output question3 but outputs question1
Console.WriteLine(questions[2].Content);
Console.ReadLine();
like image 126
Marc Gravell Avatar answered Sep 30 '22 05:09

Marc Gravell


Well you could store an IEnumerator<T> instead, and change GetNextQuestion to:

return _exam.MoveNext() ? _exam.Current : null;

However, at that point you're not actually adding any value with the Exam or WizardStepService classes, and you might as well just use IEnumerator<T> directly in Main. Note that your Main method itself is a little odd - it has repeated code where a foreach loop would be simpler - and you're unconditionally asking three questions.

Note that if you have a type with an IEnumerator<T> as a field, you probably want to implement IDisposable in order to dispose of the iterator, too - it all gets a bit messy.

like image 41
Jon Skeet Avatar answered Sep 30 '22 04:09

Jon Skeet