Task.Run和预期的委托
我不确定如何从以下观察结果中理解。
var f = new Func(uc.ViewModel.SlowProcess); 1) (VALID) string dataPromise = await Task.Run(() => f(token), token); 2) (VALID) string dataPromise = await Task.Run(() => uc.ViewModel.SlowProcess(token), token); 3) (ERROR) string dataPromise = await Task.Run(f(token), token);
uc.ViewModel.SlowProcess是一个将CancellationToken作为参数并返回字符串的方法。
项目1)和2)有效且工作正常。 第3项无效,给出以下错误:
错误1’System.Threading.Tasks.Task.Run(System.Func>,System.Threading.CancellationToken)’的最佳重载方法匹配有一些无效的参数
错误2参数1:无法从’string’转换为’System.Func>’
为什么我不能将f(令牌)作为代理传递? 如果我使用不带参数的方法,它也可以。
将f(token)
作为委托传递实际上就是你在(1)中所做的。
() => f(token)
是没有参数的委托,返回类型为string
。
f(token)
不是委托,而是立即调用返回字符串的方法f
。 这意味着,您的代码不会被任务基础结构调用,而是在您创建任务之前自己调用,从而生成一个字符串。 您无法从该字符串创建任务,这会导致语法错误。
我会坚持你在(1)中所做的。
编辑:让我们澄清一下事情。
IL代码可能显示所有。
可能,但我们应该尝试理解代码实际意味着什么。 我们可以使用.NET编译器平台Roslyn来做到这一点:
- 在Visual Studio中创建一个新的unit testing项目。
- 显示程序包管理器控制台(从View> Other Windows)并输入
Install-Package Microsoft.CodeAnalysis -Pre
-
创建一个包含以下代码的新类:
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--; } }
-
现在,让我们创建一个测试类并分析上面的语句:
[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)"); } -
对于每种情况,我们得到一些共同的输出:
(...) InvocationExpression | Task.Run
(..., token) SimpleMemberAccessExpression | Task.Run IdentifierName | Task GenericName | Run TypeArgumentList | PredefinedType | string ArgumentList | (..., token) Argument | ... (...) | ... Argument | token IdentifierName | token -
对于(1)和(2),我们得到以下参数:
ParenthesizedLambdaExpression | () => ...() ParameterList | () InvocationExpression | => ...() (...) | ...
-
对于(3)而言,我们得到以下参数:
InvocationExpression | f(token) IdentifierName | f ArgumentList | (token) Argument | token IdentifierName | token
好的,我们在这里有什么?
ParenthesizedLambdaExpression
显然是一个内联方法声明。 此表达式的类型由参数列表 (输入), lambda主体的类型(输出)以及使用lambda的预期类型 (类型推断)确定。
那是什么意思?
CancellationToken
作为第二个参数的重载,我们有以下可能性: Task.Run(Func , CancellationToken)
好的。 这就是为什么(1)和(2)都起作用的原因:它们使用lambda,它实际上生成了一个委托,并且委托的类型与Task.Run
方法的期望相匹配。
为什么f(token)
不工作呢?
一旦你接受传递参数化委托本质上被视为传递它包装的函数,一切都像你期望的那样工作。
没有“参数化代表”这样的东西。 有代理有参数( Action
, Func
…)但这与f(token)
根本不同,后者是委托f的调用,这导致委托方法的返回值。 这就是为什么f(token)
的类型只是string
:
我们怎么能让它发挥作用?
public static class TaskExtensions { public static Task Run (Func function, CancellationToken token) { Func wrappedFunction = () => function(token); return Task.Run(wrappedFunction, token); } }
这可以像TaskExtensions.Run(f, token)
一样TaskExtensions.Run(f, token)
。 但我不建议这样做,因为它没有提供额外的价值。
附加信息:
EBNF语法:C#1.0 / 2.0 / 3.0 / 4.0
C#语言规范
您传递给Task.Run
与任何预期的签名都不匹配。 它接受一个CancellationToken
并返回一个与任何允许的签名都不匹配的string
。 。 摆脱取消令牌允许它匹配这些:
上述就是C#学习教程:Task.Run和预期的委托分享的全部内容,如果对大家有所用处且需要了解更多关于C#学习教程,希望大家多多关注—计算机技术网(www.ctvol.com)!
Run(Func ) Run (Func , CancellationToken)
本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。
ctvol管理联系方式QQ:251552304
本文章地址:https://www.ctvol.com/cdevelopment/955622.html