Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/262.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
c#.net为什么Task.Run似乎可以处理Func<;T>;与其他代码不同?_C#_Asynchronous_Tap - Fatal编程技术网

c#.net为什么Task.Run似乎可以处理Func<;T>;与其他代码不同?

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

作为.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.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;
    }
}