Check if yield return contains items

1.2k views Asked by At

I'm trying to optimize a routine that looks sort of like this (simplified):

public async Task<IEnumerable<Bar>> GetBars(ObjectId id){
    var output = new Collection<Bar>();

    var page = 1;
    var hasMore = true;

    while(hasMore) {
        var foos = await client.GetFoos(id, page);

        foreach(var foo : foos) {

            if(!Proceed(foo)) {
                hasMore = false;
                break;
            }

            output.Add(new Bar().Map(foo)
        }

        page++;

    return output;
}

The method that calls GetBars() looks something like this

public async Task<Baz> GetBaz(ObjectId id){
    var bars = await qux.GetBars();

    if(bars.Any() {
        var bazBaseData = qux.GetBazBaseData(id);
        var bazAdditionalData = qux.GetBazAdditionalData(id);

        return new Baz().Map(await bazBaseData, await bazAdditionalData, bars);
    }
}

GetBaz() returns between 0 and a lot of items. Since we run through a few million id's we initially added the if(bars.Any()) statement as an initial attempt of speeding up the application.

Since the GetBars() is awaited it blocks the thread until it has collected all its data (which can take some time). My idea was to use yield return and then replace the if(bars.Any()) with a check that tests if we get at least one element, so we can fire off the two other async methods in the meantime (which also takes some time to execute).

My question is then how to do this. I know System.Linq.Count()and System.Linq.Any() defeats the whole idea of yield return and if I check the first item in the enumerable it is removed from the enumerable.

Is there another/better option besides adding for instance an out parameter to GetBars()?

TL;DR: How do I check whether an enumerable from a yield return contains any objects without starting to iterate it?

1

There are 1 answers

1
Ronan Thibaudau On BEST ANSWER

For your actual question "How do I check whether an enumerable from a yield return contains any objects without starting to iterate it?" well, you don't.

It's that simple, you can't period since the only thing you can do with an IEnumerable is well, to enumerate it. Calling Any() isn't an issue however since that "does" only enumerate the first element (and not the whole list) but it's not possible to enumerate nothing as a lot of ienumerables don't exist in any form except that of a pipeline (there could be no backing collection, it's not possible to check if something that doesn't exist yet has any elements, by design this makes no sense)

Edit : also , i don't see any yield in your code, are you mixing up awaitable and yield concepts (totally unrelated) ?