简体   繁体   中英

await Task doesn't work with IEnumerable

I am trying to execute an existing synchronous method asynchronously, however if the method is IEnumerable , then it appears to skip over the method.

Here's a simplified version of what I'm trying to achieve.

public partial class MainWindow : Window
{
    private IEnumerable<int> _Result;

    public MainWindow()
    {
        InitializeComponent();

        DoSomethingAmazing();
    }

    private async void DoSomethingAmazing()
    {
        _Result = await DoSomethingAsync();
    }

    private IEnumerable<int> DoSomething()
    {
        Debug.WriteLine("Doing something.");

        //Do something crazy and yield return something useful.
        yield return 10;
    }

    private async Task<IEnumerable<int>> DoSomethingAsync()
    {
        //Perform the DoSomething method asynchronously.
        return await Task.Run(() => DoSomething());
    }
}

Essentially, when then MainWindow gets created, it will fire off an asynchronous method to populate the _Result field.

Now DoSomething never actually executes. The debug message never appears.

If I change IEnumerable to List , then all is well. The method gets executed and the result gets populated.

The main reason I want to use IEnumerable is because I'd like to make use of yield return , it's not exactly a requirement, but it's mainly just a preference. I came across this issue and I've been scratching my head ever since.

The result of running the DoSomething method is a class that implements IEnumerable<int> and will run your code when you enumerate it. Your problem has nothing to do with using async. If you run the following code

var result = DoSomething();
Debug.WriteLine("After running DoSomething");
var resultAsList = result.ToList();
Debug.WriteLine("After enumerating result");

you will get this output.

After running DoSomething

Doing something.

After enumerating result

Now DoSomething never actually executes. The debug message never appears.

That's because when you use yield return , the compiler will generate a class which implements IEnumerable<T> for you, and return the iterator. It looks like this:

Program.<DoSomething>d__3 expr_07 = new Program.<DoSomething>d__3(-2);
expr_07.<>4__this = this;
return expr_07;

Where <DoSomething>d__3 is the compiler generated class, implementing IEnumerable<int> .

Now, because the iterator uses deferred execution, it doesn't begin execution unless you explicitly iterate it, which you aren't doing.

If I change IEnumerable to List, then all is well. The method gets executed and the result gets populated.

That's because when you use a List , you're materializing the iterator, effectively making it execute. That's why you're seeing the debug message appear.

Change your DoSomethingAmazing method and it will work.

    private async void DoSomethingAmazing()
    {
        _Result = await DoSomethingAsync();
        foreach (var item in _Result)
        {
            Debug.WriteLine(item);
        }
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM