web-dev-qa-db-fra.com

Promesse équivalente en C #

Dans Scala, il existe une classe Promise qui peut être utilisée pour compléter manuellement un avenir. Je recherche une alternative en C #.

J'écris un test et je veux qu'il ressemble à ceci:

// var MyResult has a field `Header`
var promise = new Promise<MyResult>;

handlerMyEventsWithHandler( msg =>
    promise.Complete(msg);
);

// Wait for 2 seconds
var myResult = promise.Future.Await(2000);

Assert.Equals("my header", myResult.Header);

Je comprends que ce n’est probablement pas le bon modèle pour C #, mais je ne pouvais pas trouver un moyen raisonnable d’obtenir la même chose, même avec un modèle quelque peu différent.

EDIT: veuillez noter que async/await ne m'aide pas ici, car je n'ai pas de tâche à attendre! Je viens d'avoir un accès à un gestionnaire qui sera exécuté sur un autre thread.

53
eddyP23

En C #:

  • Task<T> est un avenir (ou Task pour un avenir à unités revenant).
  • TaskCompletionSource<T> c'est une promesse.

Donc, votre code se traduirait comme tel:

// var promise = new Promise<MyResult>;
var promise = new TaskCompletionSource<MyResult>();

// handlerMyEventsWithHandler(msg => promise.Complete(msg););
handlerMyEventsWithHandler(msg => promise.TrySetResult(msg));

// var myResult = promise.Future.Await(2000);
var completed = await Task.WhenAny(promise.Task, Task.Delay(2000));
if (completed == promise.Task)
  ; // Do something on timeout
var myResult = await completed;

Assert.Equals("my header", myResult.Header);

"L'attente asynchrone temporisée" est un peu gênante, mais elle est également relativement rare dans le code du monde réel. Pour les tests unitaires, je voudrais simplement faire une attente asynchrone régulière:

var promise = new TaskCompletionSource<MyResult>();

handlerMyEventsWithHandler(msg => promise.TrySetResult(msg));

var myResult = await promise.Task;

Assert.Equals("my header", myResult.Header);
78
Stephen Cleary

L'équivalent approximatif de C # sans les bibliothèques tierces serait:

// var MyResult has a field `Header`
var promise = new TaskCompletionSource<MyResult>();

handlerMyEventsWithHandler(msg =>
  promise.SetResult(msg)
);

// Wait for 2 seconds
if (promise.Task.Wait(2000))
{
  var myResult = promise.Task.Result;
  Debug.Assert("my header" == myResult.Header);
}

Notez qu'il est généralement préférable d'utiliser await/async à un niveau aussi élevé que possible. Accéder à la Result d'un Task ou utiliser Wait peut dans certains cas introduire des blocages .

11
Dark Falcon

Vous pouvez utiliser la bibliothèque C # Promises

Open source sur Github: https://github.com/Real-Serious-Games/C-Sharp-Promise

Disponible sur NuGet: https://www.nuget.org/packages/RSG.Promise/

5
Mathew Sachin

C'est la vieille façon de faire des promesses.
À l'époque, je crois que cela s'appelait la synchronisation :)

MyResult result = null;
var are = new AutoResetEvent(false);

handlerMyEventsWithHandler( 
    msg => {result = msg; are.Set();}
);

// Wait for 2 seconds
if(!are.WaitOne(2000)) {/* handle timeout... */}

Assert.Equals("my header", myResult.Header);

Juste pour être complet - trop grand pour un commentaire.
Je suis d'accord avec réponse de Stephen Cleary .

Mais si vous construisez une façade autour d'un code hérité, vous pouvez l'utiliser pour intégrer d'anciennes API dans une tâche telle que:

public Task<MyResult> GetResultAsync() {
    MyResult result = null;
    var are = new AutoResetEvent(false);
    handlerMyEventsWithHandler(msg => {
        result = msg;
        are.Set();
    });
    are.WaitOne();
    return Task.FromResult(result);
}
2
Florian Fida

Essayez de regarder dans le modèle asynchrone. Les tâches sont l'équivalent le plus proche en c #.

Voici un lien vers un article de Microsoft expliquant leur utilisation.

2
Rich Linnell