web-dev-qa-db-ja.com

C#の同等の約束

Scalaには、Futureを手動で完了するために使用できるPromiseクラスがあります。 C#の代替を探しています。

私はテストを書いていますが、これを次のように見せたいです:

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

これはおそらくC#に適したパターンではないことを理解していますが、パターンが多少異なる場合でも同じことを達成するための合理的な方法を見つけることができませんでした。

編集:async/awaitはここで助けにならないことに注意してください、私には待つべきタスクがないので!別のスレッドで実行されるハンドラーにアクセスするだけです。

53
eddyP23

C#の場合:

  • Task<T>は未来です(または、ユニットを返す未来の場合はTask)。
  • TaskCompletionSource<T>は約束です。

したがって、コードは次のように翻訳されます。

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

「タイミング非同期待機」は少し厄介ですが、実際のコードでは比較的まれです。単体テストの場合、通常の非同期待機を行うだけです。

var promise = new TaskCompletionSource<MyResult>();

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

var myResult = await promise.Task;

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

サードパーティライブラリを使用しない大まかなC#は次のようになります。

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

通常、await/asyncを可能な限り高いレベルで使用するのが最善であることに注意してください。 ResultTaskにアクセスするか、Waitを使用すると、場合によっては デッドロックの導入 になります。

11
Dark Falcon

C#Promisesライブラリを使用できます

Githubのオープンソース: https://github.com/Real-Serious-Games/C-Sharp-Promise

NuGetで利用可能: https://www.nuget.org/packages/RSG.Promise/

5
Mathew Sachin

これは約束を行う古い方法です。
当時、私はそれが同期と呼ばれていたと信じています:)

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

完全を期すために-コメントのために大規模に。
同意します Stephen Cleary's answer

ただし、いくつかのレガシーコードの周りにファサードを構築している場合、これを使用して、次のようなタスクで古いAPIをラップできます。

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

非同期モデルを調べてみてください。タスクは、c#で最も近いものです。

使用方法を説明するMS記事へのリンクです

2
Rich Linnell