1

I had a method SplitInto2DArray that turned a File objects representing a CSV file into a File2DArray class.

File2DArray is a simple class that contains an array for Headers and a 2D array for the body.

public class File2DArray
{
    public string[] Headers;
    public string[][] Content;
}

To turn an array of File objects into File2DArray objects I did: Files.Select(File => File.SplitInto2DArray().Content

However I made SplitInto2DArray into an async function as it was blocking the thread with numerous large CSV files. When I did this I had to change the Select function but I ran into a problem.

.Select(async File => await File.SplitInto2DArray().Content

SplitInto2DArray no longer returns a File2DArray but a Task<File2DArray> which doesn't have the property Content.

I could turn it into a multiline lambda but I am interested if there is a way of accessing the output of an await Task<T> on the same line as the await.

  • 1
    This is entirely a guess, but can you do `(await File.SplitInto2DArray()).Content`? The await has to happen before getting the content. – Xtros Dec 23 '21 at 17:58
  • Dot has higher precedence than await, so you just need extra parentheses. `(await File.SplitInto2DArray()).Content` – Raymond Chen Dec 23 '21 at 17:59
  • @RaymondChen @Xtros I had a look at this in response to @Messenger's answer and adding parentheses around the `await Task` seems to change the return type of the lambda function to `Task` rather than `string[][]` which is rather strange and I'm not sure why that happens. – Alex Dobson-Pleming Dec 23 '21 at 19:24
  • @AlexDobson-Pleming https://stackoverflow.com/questions/25191512/async-await-return-task – Servy Dec 23 '21 at 19:29
  • I see so declaring the lambda as `async` is causing `Select()` to return `IEnumerable>` rather than `IEnumerable`. However it needs to return `IEnumerable` and using another `Select` with `async Task => await Task` would return another `IEnumerable>` not `IEnumerable`. Is there a way to get a list of values not tasks? – Alex Dobson-Pleming Dec 23 '21 at 20:18
  • You can use `Task.WhenAll` to collapse an `IEnumerable>` to a `T[]`. – Raymond Chen Dec 23 '21 at 20:24

2 Answers2

0

Try Files.Select(async File => (await File.SplitInto2DArray()).Content)

There is a pair of parentheses containing await File.SplitInto2DArray().

While the await keyword blocks the current thread until the async function finish and return, the Task<File2DArray> becomes File2DArray.

Maybe it's not an accurate explanation but it should work.

  • `Files.Select(async File => (await File.SplitInto2DArray()).Content)` returns type `IEnumerable>` not `IEnumerable`. Possibly this is because .Content is being evaluated as part of the `Task`? I am not sure. – Alex Dobson-Pleming Dec 23 '21 at 19:21
  • *"While the await keyword blocks the current thread until the async function finish and return"* - This is not accurate. The rest of the method does not run until the async function finishes, but the thread is not blocked. – Gabriel Luci Dec 24 '21 at 00:48
0

You can use System.Linq.Async nuget package to get nice extensions for IAsyncEnumerable

PM> Install-Package System.Linq.Async

And your code will look like

var contents = await Files.ToAsyncEnumerable()
    .SelectAwait(async file => await file.SplitInto2DArray())
    .Select(array => array.Content)
    .ToListAsync();
Sergey Berezovskiy
  • 232,247
  • 41
  • 429
  • 459