Versprechen Äquivalent in C #

73

In Scala gibt es eine Promise-Klasse, mit der eine Zukunft manuell abgeschlossen werden kann. Ich suche nach einer Alternative in C #.

Ich schreibe einen Test und möchte, dass er ungefähr so ​​aussieht:

// 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);

Ich verstehe, dass dies wahrscheinlich nicht das richtige Muster für C # ist, aber ich konnte keinen vernünftigen Weg finden, um dasselbe auch mit etwas anderem Muster zu erreichen.

EDIT: bitte beachten Sie, dass async/ awaithier nicht hilft, da ich keine Aufgabe zu erwarten habe! Ich habe nur Zugriff auf einen Handler, der auf einem anderen Thread ausgeführt wird.

eddyP23
quelle
3
Ich denke du suchst Task<T>.
Davin Tryon

Antworten:

108

In C #:

  • Task<T>ist eine Zukunft (oder Taskfür eine Zukunft, die eine Einheit zurückgibt).
  • TaskCompletionSource<T> ist ein Versprechen.

Ihr Code würde also als solcher übersetzt:

// 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);

Das "zeitgesteuerte asynchrone Warten" ist etwas umständlich, aber auch im realen Code relativ selten. Für Unit-Tests würde ich nur regelmäßig asynchron warten:

var promise = new TaskCompletionSource<MyResult>();

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

var myResult = await promise.Task;

Assert.Equals("my header", myResult.Header);
Stephen Cleary
quelle
Es gibt eine bessere Möglichkeit, Zeitüberschreitungen mithilfe von Stornierungs-Token zu implementieren. Siehe stackoverflow.com/q/23476576/1288449
Steven Liekens
7
@StevenLiekens: Ich stimme zu, dass Timeouts im Allgemeinen besser als Stornierungs-Token dargestellt werden. Dies ist jedoch am besten für Situationen geeignet, in denen Zeitüberschreitungen verwendet werden, um einen Vorgang abzubrechen. In diesem Szenario geht es darum, das Warten und nicht den Vorgang abzubrechen, und Abbruchtoken sind in diesem Szenario umständlicher.
Stephen Cleary
17

Das grobe C # -Äquivalent ohne Bibliotheken von Drittanbietern wäre:

// 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);
}

Beachten Sie, dass es normalerweise am besten ist, das await/ asyncauf einen möglichst hohen Pegel zu setzen. Der Zugriff Resultauf ein Taskoder die Verwendung Waitkann in einigen Fällen zu Deadlocks führen .

Dunkler Falke
quelle
@ Stephens Antwort ist reines C #. Was ist hier anders?
Sahib Khan
1
@SahibKhan, 1. Ich habe vor ihm gepostet und 2. Dies ist auch reines C #, obwohl es vielleicht nicht mehr die beste Vorgehensweise ist.
Dark Falcon
3

Dies ist die alte Art, Versprechen zu machen.
Damals glaube ich, dass es Synchronisation genannt wurde :)

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);

Nur der Vollständigkeit halber - zu groß für einen Kommentar.
Ich stimme Stephen Clearys Antwort zu .

Wenn Sie jedoch eine Fassade um einen alten Code erstellen, können Sie damit alte APIs in eine Aufgabe wie die folgenden einbinden:

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

Sie können das zukünftige Paket ( https://www.nuget.org/packages/Future/ ) von Nuget herunterladen und wie folgt verwenden

        Promise<int> promise = new Promise<int>();
        new Task(() =>
        {
            Thread.Sleep(100);
            promise.Set(20);

        }).Start();
        int result=promise.Get();

Gemäß dem Beispiel können Sie ein Versprechungsobjekt erstellen und ein Get-to-Get-Ergebnis ausführen. Get wartet, bis sich das Ergebnis auf dem Objekt befindet. Sie erstellen einen Satz aus einem anderen Thread, wie im obigen Beispiel gezeigt.

Dieses Paket enthält die folgenden zwei Klassen

  1. Versprechen: Was auf unbestimmte Zeit auf das Ergebnis wartet

  2. TimedPromise: Wartet nur bis zur angegebenen Zeit auf das Ergebnis. Wenn das Ergebnis in der Zeit nicht verfügbar ist, wird eine Timeout-Ausnahme ausgelöst

Kumareshan
quelle