当Action重载可用时,显式使用Func <Task>进行异步lambda函数

jdu*_*tor 38 c# lambda async-await c#-5.0

阅读这篇关于C#5 async/await的一些问题的博客文章.它在Gotcha#4中提到了一些非常深刻的东西,而且我以前没想过.

简而言之,它涵盖了一个方法,其中有一个方法有两个重载,一个取一个Action,一个取一个Func<Task>(例如Task.Run).这个问题的根源在于async void方法应该仅用于事件处理程序,然后该帖子继续描述以下场景 - 编译器推断出什么时候像下面这样的lambda函数可以编译为a Func<Task>和a Action:

Task.Run(async () => {
  await Task.Delay(1000);
});
Run Code Online (Sandbox Code Playgroud)

由于Task.Run既有的签名Task.Run(Func<Task>)Task.Run(Action),是什么类型编译异步匿名函数?一个async void还是一个Func<Task>?我的直觉是它会编译成async void纯粹因为它是非泛型类型,但C#编译器可能很聪明并且给出了Func<Task>类型偏好.

另外,有没有办法明确声明我希望使用哪个重载?我知道我可以创建一个新的实例Func<Task>并在那里传递异步lambda函数,但它仍然会编译成a async void然后将其传递给Func<Task>.的构造函数.什么是确保编译为Func<Task>?的理想方法?

小智 29

由于Task.Run既有的签名Task.Run(Func<Task>)Task.Run(Action),是什么类型编译异步匿名函数?一个async void还是一个Func<Task>?我的直觉是它会编译成async void纯粹因为它是非泛型类型,但C#编译器可能很聪明并且给出了Func<Task>类型偏好.

即使没有async,一般规则是具有返回类型的委托比没有返回类型的委托更好地匹配.另一个例子是:

static void Foo(Action a) { }
static void Foo(Func<int> f) { }
static void Bar()
{
  Foo(() => { throw new Exception(); });
}
Run Code Online (Sandbox Code Playgroud)

这是明确的,并称为第二次重载Foo.

另外,有没有办法明确声明我希望使用哪个重载?

明确这一点的一个好方法是指定参数名称.ActionFunc<Task>重载的参数名称是不同的.

Task.Run(action: async () => {
  await Task.Delay(1000);
});
Task.Run(function: async () => {
  await Task.Delay(1000);
});
Run Code Online (Sandbox Code Playgroud)

  • 真的很酷,使用参数名称来选择重载.否则,可以将整个lambda转换为所需的委托类型,但这看起来很难看.在你的第一个例子中,参数`f`的名称有点混乱(虽然合法),因为方法本身也称为`f`. (10认同)
  • @jduncanator我不知道怎么读.参数名称被认为是API的一部分,并且对参数名称的更改被视为重大更改,因此它是"标准",因为您可以相信MS不会在没有充分理由的情况下突然更改它.这是否回答你的问题? (2认同)

Sri*_*vel 27

我刚检查它Task.Run(Func<Task>)默认编译进去,我对此没有很好的解释.

这是IL的相关部分

IL_0001:  ldsfld      UserQuery.CS$<>9__CachedAnonymousMethodDelegate1
IL_0006:  brtrue.s    IL_001B
IL_0008:  ldnull      
IL_0009:  ldftn       UserQuery.<Main>b__0
IL_000F:  newobj      System.Func<System.Threading.Tasks.Task>..ctor//<--Note here
IL_0014:  stsfld      UserQuery.CS$<>9__CachedAnonymousMethodDelegate1
IL_0019:  br.s        IL_001B
IL_001B:  ldsfld      UserQuery.CS$<>9__CachedAnonymousMethodDelegate1
IL_0020:  call        System.Threading.Tasks.Task.Run
Run Code Online (Sandbox Code Playgroud)

您可以使用visual studio类型推断轻松地检查这一点,如果您只是将鼠标放在方法上,它将显示它将被编译的方法,或者只需单击方法按下F12您可以看到元数据,它将告诉您推断的类型是什么通过编译器.

另外,有没有办法明确声明我希望使用哪个重载?是,明确指定委托.

Task.Run(new Action(async () =>
{
    await Task.Delay(1000);
}));

Task.Run(new Func<Task>(async () =>
{
    await Task.Delay(1000);
}));
Run Code Online (Sandbox Code Playgroud)