Mark Vickery
Mark Vickery

Reputation: 1955

Incrementing an IEnumerator/IEnumerable while using yield

I am trying to yield iterate through a collection and if the collection is empty then call an increment method that will get the next set of results. When the increment says there are no more results then the yield with break;

I can not use (i think) a standard IEnumerator with MoveNext() etc as the increment method returns two different types of data.

I have tried an example below but it stops after one itteration. I am hoping there is a much easier way to do this (or at least is possible just I have a bug).

static void Main(string[] args)
{
    var query = new Query();

    foreach(var s in query.Q1())
    {
        Console.WriteLine(s);
    }

    foreach (var s in query.Q2())
    {
        Console.WriteLine(s);
    }

    Console.ReadLine();
}

public class Query
{
    int i = 0;
    bool complete;
    List<string> q1 = new List<string>();
    List<string> q2 = new List<string>();

    public IEnumerable<string> Q1()
    {
        if (complete)
        {
            yield break;
        }
        if (!q1.Any() && !complete)
        {
            Increment();
        }
        if (q1.Any())
        {
            foreach (var s in q1)
            {
                yield return s;
            }
        }
    }

    public IEnumerable<string> Q2()
    {
        if (complete)
        {
            yield break;
        }
        if (!q2.Any() && !complete)
        {
            Increment();
        }
        if (q2.Any())
        {
            foreach (var s in q2)
            {
                yield return s;
            }
        }
    }

    void Increment()
    {
        if (i < 10)
        {
                    // simulate getting two types of data back (parent and two children) from datasource
            q1.Add((1 * (i + 1)).ToString());
            q2.Add("A: " + (1 * (i + 1)).ToString());
            q2.Add("B: " + (1 * (i + 1)).ToString());

            i++;
        }
        else
        {
            complete = true;
        }
    }
}

result:

1
A: 1
B: 1

Any ideas on a better way of doing this or where I am going wrong?

EDIT

Here is my rough and ready fix:

public IEnumerable<string> Q1()
{
    var index = 0;

    if (!complete)
    {
        while (!complete)
        {
            var count = q1.Count();

            if (index + 1 == count)
            {
                for (var x = index; index < count; index++)
                {
                    yield return q1[index];
                }
            }
            else
            {
                Increment();
            }
        }
    }
    else
    {
        foreach (var s in q1)
        {
            yield return s;
        }
    }
}

Upvotes: 0

Views: 1186

Answers (1)

Sergey Berezovskiy
Sergey Berezovskiy

Reputation: 236268

You are adding elements only to q2 list. Thus when you call Q1 iterator, you are exiting it after checking

if (q1.Any())

When you calling Q2 iterator, you exit it after

if (q2.Any())
{
     foreach (var s in q2)
     {
         yield return s;
     }
}

This foreach loop is executed only once and it returns only three items which where added to q2 during single Increment call in Q1 iterator.

It's not very clear what you want to achieve, but here is the way you can use loop for generating return values of iterator

public IEnumerable<string> Q2()
{
    for (int i = 1; i <= 10; i++) // start from 1
    {
        yield return i.ToString(); // do not multiply by 1
        yield return "A: " + i;    // .ToString() is not necessary 
        yield return "B: " + i;
    }
}

Upvotes: 1

Related Questions