web-dev-qa-db-fra.com

Utiliser explicitement une Func <Task> pour la fonction lambda asynchrone lorsqu'une surcharge d'action est disponible

Lecture this article de blog sur certains des pièges de l'asynchronisation/attente de C # 5. Il mentionne dans Gotcha # 4 quelque chose de très profond et auquel je n'avais pas pensé auparavant.

En bref, il couvre le scénario où vous avez une méthode qui a deux surcharges, une qui prend un Action et une qui prend un Func<Task> (Par exemple Task.Run). Ce problème est enraciné dans l'argument selon lequel les méthodes async void Ne doivent être utilisées que pour les gestionnaires d'événements, la publication décrivant ensuite le scénario suivant - Que déduit le compilateur lorsqu'une fonction lambda comme la suivante peut être compilée à la fois un Func<Task> et un Action:

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

Étant donné que Task.Run Possède les signatures de Task.Run(Func<Task>) et Task.Run(Action), à quel type la fonction anonyme asynchrone est-elle compilée? Un async void Ou un Func<Task>? Mon intuition dit qu'il se compilera jusqu'à un async void Uniquement parce que c'est un type non générique, mais le compilateur C # pourrait être intelligent et donner la préférence aux types Func<Task>.

Existe-t-il également un moyen de déclarer explicitement la surcharge que je souhaite utiliser? Je sais que je pourrais simplement créer une nouvelle instance de Func<Task> Et y passer la fonction asynchrone lambda, mais elle se compilerait toujours vers un async void, Puis la transmettrait au constructeur du Func<Task>. Quelle est la façon idéale de s'assurer que sa compilation est un Func<Task>?

38
jduncanator

Je viens de vérifier qu'il est compilé dans Task.Run(Func<Task>) par défaut, je n'ai pas de bonne explication à cela.

Voici la partie pertinente de l'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

vous pouvez vérifier cela facilement en utilisant l'inférence de type Visual Studio, il vous montrera quelle méthode il sera compilé si vous placez simplement la souris sur la méthode, ou cliquez simplement sur la méthode, appuyez sur F12 vous pouvez voir les métadonnées qui vous diront quel était le type déduit par le compilateur.

Existe-t-il également un moyen de déclarer explicitement la surcharge que je souhaite utiliser? Oui, spécifiez explicitement le délégué.

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

Task.Run(new Func<Task>(async () =>
{
    await Task.Delay(1000);
}));
27
Sriram Sakthivel

Étant donné que Task.Run Possède les signatures de Task.Run(Func<Task>) et Task.Run(Action), à quel type la fonction anonyme asynchrone est-elle compilée? Un async void Ou un Func<Task>? Mon intuition dit qu'il se compilera jusqu'à un async void Uniquement parce que c'est un type non générique, mais le compilateur C # pourrait être intelligent et donner la préférence aux types Func<Task>.

La règle générale, même sans async, est qu'un délégué avec un type de retour est une meilleure correspondance qu'un délégué sans type de retour. Un autre exemple de ceci est:

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

Ceci est sans ambiguïté et appelle la deuxième surcharge de Foo.

Existe-t-il également un moyen de déclarer explicitement la surcharge que je souhaite utiliser?

Une bonne façon de clarifier cela est de spécifier le nom du paramètre. Les noms des paramètres des surcharges Action et Func<Task> Sont différents.

Task.Run(action: async () => {
  await Task.Delay(1000);
});
Task.Run(function: async () => {
  await Task.Delay(1000);
});
29
user743382