1

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.

Mike Eason
  • 9,525
  • 2
  • 38
  • 63
  • 5
    You never enumerate the `IEnumerable` that is returned. – juharr Jul 23 '15 at 13:11
  • You should avoid having async void methods, and make it an async Task return type (for DoSomethingAmazing). See this answer to a different question for more information on async void http://stackoverflow.com/a/12144426/3745837 – BrettJ Jul 23 '15 at 13:17

3 Answers3

3

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

Community
  • 1
  • 1
juharr
  • 31,741
  • 4
  • 58
  • 93
2

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.

Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
0

Change your DoSomethingAmazing method and it will work.

    private async void DoSomethingAmazing()
    {
        _Result = await DoSomethingAsync();
        foreach (var item in _Result)
        {
            Debug.WriteLine(item);
        }
    }
ndd
  • 3,051
  • 4
  • 25
  • 40