6

I have the following method:

public async IEnumerable<string> GetListDriversAsync()
{
   var drives = await graphClient.Drive.Root.Children.Request().GetAsync();
        foreach (var d in drives)
            yield return d.ToString(); 
}

But compiler error says:

"The return type of an async must be void, Task or Task <T>"

How do I return IEnumerable when the method is async?

xdtTransform
  • 1,986
  • 14
  • 34
Codey
  • 487
  • 1
  • 8
  • 27
  • 1
    Rename your method signature to `public async Task> GetListDriversAsync()` – Kunal Mukherjee May 07 '19 at 12:26
  • 1
    I tried this already, it says: "the body cannot be an iterator because Task> is not an interface type" – Codey May 07 '19 at 12:31
  • The short answer is that what you want to do cannot be done in the current version of C#. You can keep your iterator method if you encapsulate it in a local function, but you will need to fully populate a collection and return this to use the `async` keyword and the `Task` return type. There is no support right now for "async yield return". – Lasse V. Karlsen May 07 '19 at 12:34
  • Possible duplicate of [async Task with yield return?](https://stackoverflow.com/questions/42882078/async-taskienumerable-with-yield-return) – Frontear May 07 '19 at 12:35
  • Both async/await and iterator methods involves compiler magic that rewrites your method, they cannot work together right now. – Lasse V. Karlsen May 07 '19 at 12:35
  • Is C# 8 Async enumerable allowed? https://dotnetcoretutorials.com/2019/01/09/iasyncenumerable-in-c-8/ – xdtTransform May 07 '19 at 12:35
  • 1
    What you are trying to do is "async enumerables" - that's a vNext feature; right now: there aren't great options for this – Marc Gravell May 07 '19 at 12:35

2 Answers2

8

An alternative way is possible in C# 8. It uses IAsyncEnumerable.

public async IAsyncEnumerable<string> GetListDriversAsync()
{
    var drives = await graphClient.Drive.Root.Children.Request().GetAsync();
    foreach (var d in drives)
        yield return d.ToString();
}

It will alter your signature a bit, which may (or may not) be an option for you.

Usage:

await foreach (var driver in foo.GetListDriversAsync())
{
    Console.WriteLine(driver );
}
smoksnes
  • 10,509
  • 4
  • 49
  • 74
7

Try this:

public async Task<IEnumerable<string>> GetListDriversAsync()
{
    var drives = await graphClient.Drive.Root.Children.Request().GetAsync();

    IEnumerable<string> GetListDrivers()
    {
        foreach (var d in drives)
            yield return d.ToString();
    }

    return GetListDrivers();
}
Enigmativity
  • 113,464
  • 11
  • 89
  • 172
  • 2
    Just for my own understanding - why the local function and `yield return` in this case instead of `return drives.Select(d => d.ToString())` ? – Scott Hannen May 07 '19 at 13:08
  • 1
    @ScottHannen see [Local functions and exceptions](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/local-functions#local-functions-and-exceptions) which discusses exception handling with enumerators in relation to local functions – Josh Withee May 07 '19 at 15:21
  • @ScottHannen - The OP wanted `yield` so that's why I wrote it that way. Marathon55 is also rigth about it being the right pattern to handle exceptions. – Enigmativity May 07 '19 at 22:15