James Jeffery
James Jeffery

Reputation: 12599

LINQ Select Statement. Anonymous Method Returns Exception

When using an anonymous method in a LINQ Select statement does the anonymous method have to return a value?

When I do the following I get no errors:

await Task.WhenAll(list.Select(a => doSomething(a)));

But when I do this I get an error that says type arguments cannot be inferred from the usage:

await Task.WhenAll(list.Select(a => {
    doSomething(a);
    Log("Log Something");
    UpdateUI();
}));

Why does the first work and the second doesn't?

Here is the doSomething method:

private async Task doSomething(string a)
{
     HttpClient client = new HttpClient;
     // Do stuff
     string source = await client.PostAsync(a, content);
     // Extract data from source and store in text file.
}

Upvotes: 1

Views: 2620

Answers (3)

Servy
Servy

Reputation: 203842

When using an anonymous method in a LINQ Select statement does the anonymous method have to return a value?

Yes. The signature of the Select method is:

public IEnumerable<TResult> Select<TSource, TResult>(
    IEnumerable<TSource> source, 
    Func<TSource, TResult> selector)

so the selector must return a value.

With your first code snippet the return statement is implicit. doSomething returns a value, and that value is what each item is projected to.

When you use a statement lambda, instead of an expression lambda, there is no implicit return statement. Since your second code block is not returning anything, it doesn't match what Select expects.

Now, as for your actual problem. What you want to do is project each task into a task that does something, then writes to the log when it's done and updates the UI. You can use an async lambda to do this. In an async lambda when there are no return statement it will still be returning a Task (just without a Result) instead of void. And that's exactly what you want to do, project each task into another task.

await Task.WhenAll(list.Select(async a => {
    await doSomething(a);
    Log("Log Something");
    UpdateUI();
}));

Upvotes: 5

JLRishe
JLRishe

Reputation: 101690

Yes, the function you pass to Select() has to return a value, because the purpose of Select is to change one set of values into another set of values. How about this:

Define this method:

private async Task DoSomethingLogAndUpdate(string a)
{
     await doSomething(a);
     Log("Log Something");
     UpdateUI();
}

Then do:

await Task.WhenAll(list.Select(a => DoSomethingLogAndUpdate(a)));

Or to do this without defining a separate method:

await Task.WhenAll(list.Select(async a => {
    await doSomething(a);
    Log("Log Something");
    UpdateUI();
}));

Upvotes: 2

Eric Finn
Eric Finn

Reputation: 9005

The first one is a simple expression, so the type of that expression is used as the return type of the lambda.

From MSDN:

A lambda expression with an expression on the right side of the => operator is called an expression lambda. Expression lambdas are used extensively in the construction of Expression Trees (C# and Visual Basic). An expression lambda returns the result of the expression

(Emphasis mine)

However, what you have is a statement lambda, which means that in order to return a value, you must have a return statement in the lambda body.

Upvotes: 2

Related Questions