104 votes

Équivalent de Promise en C#

En Scala, il existe une classe Promise qui pourrait être utilisée pour compléter manuellement un Future. Je cherche 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 le C#, mais je n'ai pas réussi à 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 n'est pas utile ici, car je n'ai pas de tâche à attendre ! J'ai juste un accès à un gestionnaire qui sera exécuté sur un autre thread.

3 votes

Je pense que vous cherchez Task<T> .

154voto

Stephen Cleary Points 91731

En C# :

  • Task<T> est un futur (ou Task pour un avenir à rendement unitaire).
  • TaskCompletionSource<T> est une promesse.

Votre code se traduirait donc comme suit :

// 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 maladroite, mais elle est aussi relativement rare dans le code du monde réel. Pour les tests unitaires, je ferais simplement une attente asynchrone normale :

var promise = new TaskCompletionSource<MyResult>();

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

var myResult = await promise.Task;

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

0 votes

Il existe un moyen plus agréable de mettre en œuvre des délais d'attente en utilisant des jetons d'annulation. Voir stackoverflow.com/q/23476576/1288449

8 votes

@StevenLiekens : Je suis d'accord sur le fait que les délais d'attente sont généralement mieux représentés comme des jetons d'annulation ; cependant, c'est mieux pour les situations où les délais d'attente sont utilisés pour annuler une opération. Dans ce scénario, nous parlons de l'annulation de l'opération suivante attendre et non le opération et les jetons d'annulation sont plus gênants dans ce scénario.

22voto

Dark Falcon Points 15609

L'équivalent approximatif en C# sans 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 l'option await / async à un niveau aussi élevé que possible. Accéder à la Result d'un Task ou en utilisant Wait peut dans certains cas introduire des blocages .

0 votes

La réponse de @Stephen est du pur C#. Ce qui est différent ici.

4 votes

@SahibKhan, 1. J'ai posté avant lui et 2. C'est du pur C# aussi, même si ce n'est peut-être plus la meilleure pratique.

7voto

Mathew Sachin Points 856

Vous pouvez utiliser la bibliothèque C# Promises

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

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

0 votes

Vous pouvez en savoir plus à ce sujet sur mon blog : what-could-possibly-go-wrong.com/promises-for-game-development

5voto

Florian Fida Points 402

C'est la façon ancienne 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 La réponse de Stephen Cleary .

Mais si vous construisez une façade autour d'un code hérité, cela peut être utilisé pour envelopper les anciennes API dans une tâche comme celle-ci :

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

2voto

Rich Linnell Points 37

Essayez d'étudier le modèle asynchrone. Les tâches sont l'équivalent le plus proche en C#.

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X