c#.net为什么Task.Run似乎可以处理Func<;T>;与其他代码不同?
作为.NET4.5的一部分的新Task.Run静态方法的行为似乎不像人们所期望的那样 例如:c#.net为什么Task.Run似乎可以处理Func<;T>;与其他代码不同?,c#,asynchronous,tap,C#,Asynchronous,Tap,作为.NET4.5的一部分的新Task.Run静态方法的行为似乎不像人们所期望的那样 例如: Task<Int32> t = Task.Run(()=>5); Task t=Task.Run(()=>5); 编译得很好,但是 Task<Int32> t = Task.Run(MyIntReturningMethod); ... public Int32 MyIntReturningMethod() { return (5); } Task t
Task<Int32> t = Task.Run(()=>5);
Task t=Task.Run(()=>5);
编译得很好,但是
Task<Int32> t = Task.Run(MyIntReturningMethod);
...
public Int32 MyIntReturningMethod() {
return (5);
}
Task t=Task.Run(MyIntReturningMethod);
...
公共Int32 MyIntReturningMethod(){
返回(5);
}
抱怨MyIntReturningMethod返回了错误的类型
也许我只是不明白调用了哪个重载的Task.Run。但在我看来,我上面的lambda代码看起来很像Func
,MyIntReturningMethod肯定与Func
兼容
你知道发生了什么事吗?
Michael似乎是一个过载解决问题。编译器无法判断您正在调用哪个重载(因为首先它必须找到要创建的正确委托,而它不知道,因为这取决于您正在调用的重载)。它必须猜测和检查,但我猜它没有那么聪明。当您将
Func
传递给方法Run(Func)
时,您不必在methodcall上指定泛型,因为它可以推断它。你的lambda做了这个推断
但是,您的函数实际上不是Func
,而lambda是
如果您执行Func f=MyIntReturningMethod
,它会工作。现在,如果您指定Task.Run(MyIntReturningMethod)
,您希望它也能工作。但是,它无法决定是应该解决Func
重载还是Func
重载,这没有多大意义,因为该方法显然没有返回任务
如果您编译了如下简单的内容:
void Main()
{
Thing(MyIntReturningMethod);
}
public void Thing<T>(Func<T> o)
{
o();
}
public Int32 MyIntReturningMethod()
{
return (5);
}
void Main()
{
事物(MyIntReturningMethod);
}
公共无效物(Func o)
{
o();
}
公共Int32 MyIntReturningMethod()
{
返回(5);
}
IL看起来像这样
IL_0001: ldarg.0
IL_0002: ldarg.0
IL_0003: ldftn UserQuery.MyIntReturningMethod
IL_0009: newobj System.Func<System.Int32>..ctor
IL_000E: call UserQuery.Thing
IL_0001:ldarg.0
IL_0002:ldarg.0
IL_0003:ldftn UserQuery.MyIntReturningMethod
IL_0009:newobj System.Func..ctor
IL_000E:调用UserQuery.Thing
(一些额外的内容来自LinqPad的添加……比如UserQuery部分)
IL看起来完全相同,就像您执行显式强制转换一样。因此,编译器似乎并不知道该使用哪种方法。所以它不知道自动创建什么类型的强制转换
您只需使用Task.Run((Func)MyIntReturningMethod)
来帮助它。虽然我同意这似乎是编译器应该能够处理的事情。因为Func
与Func
不同,所以它们会混淆编译器是没有意义的
这本应在.NET4.0中修复,但Task.Run()是.NET4.5中的新功能
.NET4.5通过添加Task.Run(Func)
方法,具有自己的重载模糊性。以及在C#version 5中对异步/等待的支持。它允许从T foo()
隐式转换为Func
对于async/await来说,这是一种非常好的语法,但在这里会产生空洞。方法声明中的async
关键字的省略在方法重载选择中没有考虑,这打开了另一个痛苦的潘多拉盒子,程序员在想要使用async时忘记了使用async。否则,遵循通常的C#约定,方法重载选择只考虑方法签名中的方法名称和参数
明确使用委托类型是解决歧义所必需的。以下是我的建议:
public class MyTest
{
public void RunTest()
{
Task<Int32> t = Task.Run<Int32>(new Func<int>(MyIntReturningMethod));
t.Wait();
Console.WriteLine(t.Result);
}
public int MyIntReturningMethod()
{
return (5);
}
}
公共类MyTest
{
公共无效运行测试()
{
Task t=Task.Run(新函数(MyIntReturningMethod));
t、 等待();
控制台写入线(t.Result);
}
公共int MyIntReturningMethod()
{
返回(5);
}
}
这种方法对我来说很有效
此外,您可以使用lambda表达式尝试此操作:
public class MyTest
{
public void RunTest()
{
Task<Int32> t = Task.Run<Int32>(() => MyIntReturningMethod());
t.Wait();
Console.WriteLine(t.Result);
}
public int MyIntReturningMethod()
{
return (5);
}
}
int.Parse("5")
公共类MyTest
{
公共无效运行测试()
{
Task t=Task.Run(()=>MyIntReturningMethod());
t、 等待();
控制台写入线(t.Result);
}
公共int MyIntReturningMethod()
{
返回(5);
}
}
(当然,要解决问题,只需说Task.Run((Func)MyIntReturningMethod)
)
这绝对与任务
等无关
这里需要注意的一个问题是,当存在很多重载时,编译器错误文本将只关注一对重载。所以这是令人困惑的。原因是,确定最佳重载的算法考虑了所有重载,当该算法得出结论认为找不到最佳重载时,不会为错误文本生成特定的一对重载,因为可能(也可能没有)涉及到所有重载
要了解发生了什么,请参见以下简化版本:
static class Program
{
static void Main()
{
Run(() => 5); // compiles, goes to generic overload
Run(M); // won't compile!
}
static void Run(Action a)
{
}
static void Run<T>(Func<T> f)
{
}
static int M()
{
return 5;
}
}
实际上甚至不能转换为系统.Action
类型。如果您尝试这样做:
Action myLittleVariable = () => 5;
它将失败并出现错误CS0201:只能将赋值、调用、递增、递减、等待和新对象表达式用作语句。所以很明显,lambda使用哪个重载
另一方面,方法组:
M
可转换为Func
和Action
。请记住,完全可以不拾取返回值,就像下面的语句:
M(); // don't use return value
它本身是有效的
这类回答了问题,但我将给出一个额外的例子来说明另外一点。考虑这个例子:
static class Program
{
static void Main()
{
Run(() => int.Parse("5")); // compiles!
}
static void Run(Action a)
{
}
static void Run<T>(Func<T> f)
{
}
}
它本身作为语句是有效的。但在这种情况下,重载解析仍然可以找到更好的重载。正如我之前所说,检查C#Spec
受汉帕桑和蓝拉贾·丹尼普夫卢格霍夫特的启发,这里是最后一个(我认为)例子:
类程序
{
静态void Main()
{
Run(M);//不会com
int.Parse("5")
class Program
{
static void Main()
{
Run(M); // won't compile!
}
static void Run(Func<int> f)
{
}
static void Run(Func<FileStream> f)
{
}
static int M()
{
return 5;
}
}