I am not sure how to make sense out of the following observed results.
var f = new Func(uc.ViewModel.SlowProcess);
1) (VALID
Passing f(token) as a delegate is actually what you're doing in (1).
() => f(token) is a delegate with no arguments and return type string.
f(token) is not a delegate, but an immediate invocation of method f that returns a string. That means, your code isn't called by the Task infrastructure, but by yourself, before the Task is even created, resulting in a string. You can't create a Task from that string, which leads to the syntax error.
I would stick with what you did in (1).
Edit: Let's clarify things a bit.
IL code probably shows all.
Probably, but we should rather try to understand what the code actually means. We can do this using Roslyn, the .NET Compiler Platform:
Install-Package Microsoft.CodeAnalysis -PreCreate a new class containing the following code:
using System;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
public class SyntaxTreeWriter : CSharpSyntaxWalker
{
public static void Write(string code)
{
var options = new CSharpParseOptions(kind: SourceCodeKind.Script);
var syntaxTree = CSharpSyntaxTree.ParseText(code, options);
new SyntaxTreeWriter().Visit(syntaxTree.GetRoot());
}
private static int Indent = 0;
public override void Visit(SyntaxNode node)
{
Indent++;
var indents = new String(' ', Indent * 2);
Console.WriteLine(indents + node.CSharpKind());
base.Visit(node);
Indent--;
}
}
Now, let's create a Test Class and analyze your statements from above:
[TestMethod]
public void Statement_1()
{
SyntaxTreeWriter.Write("Task.Run(() => f(token), token)");
}
[TestMethod]
public void Statement_2()
{
SyntaxTreeWriter.Write("Task.Run(() => uc.ViewModel.SlowProcess(token), token)");
}
[TestMethod]
public void Statement_3()
{
SyntaxTreeWriter.Write("Task.Run(f(token), token)");
}
For each case, we get some common output:
(...)
InvocationExpression | Task.Run(..., token)
SimpleMemberAccessExpression | Task.Run
IdentifierName | Task
GenericName | Run
TypeArgumentList |
PredefinedType | string
ArgumentList | (..., token)
Argument | ...
(...) | ...
Argument | token
IdentifierName | token
For (1) and (2), we get the following argument:
ParenthesizedLambdaExpression | () => ...()
ParameterList | ()
InvocationExpression | => ...()
(...) | ...
For (3) instead, we get the following argument:
InvocationExpression | f(token)
IdentifierName | f
ArgumentList | (token)
Argument | token
IdentifierName | token
A ParenthesizedLambdaExpression obviously is an inline method declaration. The type of this expression is determined by the parameter list (input), the type of the lambda body (output) and by the expected type where the lambda is used (type inference).
What does that mean?
FuncExpression> ActionExpressionCancellationToken as second parameter:
ActionFuncFuncFunc> Func, where TResult is stringActionTask.Run(Func, CancellationToken) Okay. That's why (1) and (2) both work: They use a lambda, which in fact generates a delegate, and the type of the delegate matches the expectations of the Task.Run method.
Why is f(token) not working then?
Once you accept that passing a parameterized delegate essentially gets treated like passing the function(s) it wraps, everything works like you would expect.
There is no such thing as a "parameterized delegate". There are delegates that have parameters (Action, Func...) but this is fundamentally different from f(token) which is an invocation of delegate f, which results in the return value of the delegated method. That's why the type of f(token) simply is string:
f(token) is string, because f has been declared as Func.Task.Run still take:
ActionFuncFuncFunc> How could we make it work?
public static class TaskExtensions
{
public static Task Run(Func function, CancellationToken token)
{
Func wrappedFunction = () => function(token);
return Task.Run(wrappedFunction, token);
}
}
This could be called like TaskExtensions.Run(f, token). But I would not recommend doing that, as it provides no additional value whatsoever.
Additional information:
EBNF Syntax: C# 1.0/2.0/3.0/4.0
C# Language Specification