2016-08-17 2 views
30

В Scala есть класс Promise, который можно использовать для завершения Будущего вручную. Я ищу альтернативу в C#.Обещающий эквивалент в 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#, но я не мог понять, разумный способ для достижения той же самой вещи даже с несколько иной структурой.

EDIT: обратите внимание, что async/await здесь не помогает, так как у меня нет задаче ждать! Я просто имею доступ к обработчику, который будет запущен в другом потоке.

+0

Возможного дубликат [как и когда использовать \ 'асинхронной \' и \ '\' ОЖИДАНИЕ] (http://stackoverflow.com/questions/14455293/how-and-when-to-use-async-and-wait) – Stijn

+1

Я думаю, что вы ищете 'Task '. –

ответ

41

В 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); 
+0

Существует более удобный способ использования тайм-аутов с использованием токенов отмены. См. Https://stackoverflow.com/q/23476576/1288449 –

+1

@StevenLiekens: Я согласен, что таймауты, как правило, лучше представлены в качестве токенов отмены; однако это лучше всего для ситуаций, когда тайм-ауты используются для отмены операции. В этом случае мы говорим об отмене * wait *, а не * операции *, а токены отмены в этом сценарии более неудобны. –

6

Грубая 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 как можно более высоким уровнем. Доступ к Result из Task или с использованием Wait может в некоторых случаях introduce deadlocks.