dav_i
dav_i

Reputation: 28137

Accessing yield return collection

Is there any way to access the IEnumerable<T> collection being build up by yield return in a loop from within the method building the IEnumerable itself?

Silly example:

Random random = new Random();

IEnumerable<int> UniqueRandomIntegers(int n, int max)
{
    while ([RETURN_VALUE].Count() < n)
    {
        int value = random.Next(max);
        if (![RETURN_VALUE].Contains(value))
            yield return value;
    }
}

Upvotes: 0

Views: 603

Answers (1)

Jon Skeet
Jon Skeet

Reputation: 1502646

There is no collection being built up. The sequence that is returned is evaluated lazily, and unless the caller explicitly copies the data to another collection, it will be gone as soon as it's been fetched.

If you want to ensure uniqueness, you'll need to do that yourself. For example:

IEnumerable<int> UniqueRandomIntegers(int n, int max)
{
    HashSet<int> returned = new HashSet<int>();
    for (int i = 0; i < n; i++)
    {
        int candidate;
        do
        {
            candidate = random.Next(max);
        } while (returned.Contains(candidate));
        yield return candidate;
        returned.Add(candidate);
    }
}

Another alternative for unique random integers is to build a collection of max items and shuffle it, which can still be done just-in-time. This is more efficient in the case where max and n are similar (as you don't need to loop round until you're lucky enough to get a new item) but inefficient in the case where max is very large and n isn't.

EDIT: As noted in comments, you can shorten this slightly by changing the body of the for loop to:

int candidate;
do
{
    candidate = random.Next(max);
} while (!returned.Add(candidate))
yield return candidate;

That uses the fact that Add will return false if the item already exists in the set.

Upvotes: 5

Related Questions