web-dev-qa-db-fra.com

TaskCompletionSource non générique ou alternative

Je travaille avec une fenêtre d'alerte (Telerik WPF) qui est normalement affichée de manière asynchrone (le code continue de fonctionner pendant qu'il est ouvert) et je veux le rendre synchrone en utilisant async/wait.

J'ai ce travail avec TaskCompletionSource mais cette classe est générique et retourne un objet comme Task<bool> quand tout ce que je veux, c'est un simple Task sans valeur de retour.

public Task<bool> ShowAlert(object message, string windowTitle)
{
    var dialogParameters = new DialogParameters { Content = message };

    var tcs = new TaskCompletionSource<bool>();
    dialogParameters.Closed += (s, e) => tcs.TrySetResult(true);

    RadWindow.Alert(dialogParameters);

    return tcs.Task;
}

Le code qui appelle cette méthode est

await MessageBoxService.ShowAlert("The alert text.")

Comment puis-je retourner une tâche non générique qui fonctionne de la même manière que je peux attendre jusqu'à ce que le dialogParameters.Closed un événement se déclenche? Je comprends que je pourrais simplement ignorer le bool qui est renvoyé dans ce code. Je cherche une solution différente de cela.

60
Kevin Kalitowski

La méthode peut être modifiée pour:

public Task ShowAlert(object message, string windowTitle)

Task<bool> hérite de Task pour que vous puissiez retourner Task<bool> tout en exposant uniquement Task à l'appelant

Modifier:

J'ai trouvé un document Microsoft, http://www.Microsoft.com/en-us/download/details.aspx?id=19957 , par Stephen Toub intitulé "Le modèle asynchrone basé sur les tâches" et il a l'extrait suivant qui recommande ce même modèle.

Il n'y a pas d'homologue non générique à TaskCompletionSource <TResult>. Cependant, Task <TResult> dérive de Task, et donc le TaskCompletionSource <TResult> générique peut être utilisé pour les méthodes liées aux E/S qui renvoient simplement une Task en utilisant une source avec un TResult factice (Boolean est un bon choix par défaut, et si un développeur s'inquiète pour un consommateur de la tâche de le convertir en tâche <TResult>, un type de TResult privé peut être utilisé)

85
Kevin Kalitowski

Si vous ne souhaitez pas divulguer d'informations, l'approche courante consiste à utiliser TaskCompletionSource<object> et complet avec un résultat de null. Ensuite, renvoyez-le simplement en tant que Task.

45
Stephen Cleary

Nito.AsyncEx implémente une classe TaskCompletionSource non générique, crédit à Mr @StephenCleary ci-dessus.

3
georgiosd

De @Kevin Kalitowski

J'ai trouvé un document Microsoft, http://www.Microsoft.com/en-us/download/details.aspx?id=19957 , par Stephen Toub intitulé "Le modèle asynchrone basé sur les tâches"

Il y a un exemple dans ce document qui, je pense, traite de la question comme le souligne Kevin. Voici l'exemple:

public static Task Delay(int millisecondsTimeout)
{
    var tcs = new TaskCompletionSource<bool>();
    new Timer(self =>
    {
        ((IDisposable)self).Dispose();
        tcs.TrySetResult(true);
    }).Change(millisecondsTimeout, -1);
    return tcs.Task;
}

Au début, je pensais que ce n'était pas bon car vous ne pouvez pas ajouter directement le modificateur "async" à la méthode sans message de compilation. Mais, si vous modifiez légèrement la méthode, la méthode se compilera avec async/wait:

public async static Task Delay(int millisecondsTimeout)
{
    var tcs = new TaskCompletionSource<bool>();
    new Timer(self =>
    {
        ((IDisposable)self).Dispose();
        tcs.TrySetResult(true);
    }).Change(millisecondsTimeout, -1);
    await tcs.Task;
}

Edit: au début, je pensais avoir surmonté la bosse. Mais, lorsque j'ai exécuté le code équivalent dans mon application, ce code fait simplement que l'application se bloque lorsqu'elle attend tcs.Task ;. Donc, je crois toujours que c'est une grave faille de conception dans la syntaxe async/wait c #.

1
Melbourne Developer