Преобразование объекта обратного вызова в асинхронную задачу - PullRequest
0 голосов
/ 11 июля 2019

У меня есть интерфейс, который выглядит следующим образом:

public interface IFuture<T>
{
    FutureState state { get; }
    T value { get; }
    Exception error { get; }
    IFuture<T> OnItem(FutureValueCallback<T> callback);
    IFuture<T> OnSuccess(FutureValueCallback<T> callback);
    IFuture<T> OnError(FutureErrorCallback callback);
    IFuture<T> OnComplete(FutureCallback<T> callback);
}

Вот доступные состояния, а также декларация для делегатов:

public enum FutureState
{
    /// <summary>
    /// The future hasn't begun to resolve a value.
    /// </summary>
    Pending,

    /// <summary>
    /// The future is working on resolving a value.
    /// </summary>
    Processing,

    /// <summary>
    /// The future has a value ready.
    /// </summary>
    Success,

    /// <summary>
    /// The future failed to resolve a value.
    /// </summary>
    Error
}
public delegate void FutureCallback<T>(IFuture<T> future);

public delegate void FutureValueCallback<T>(T value);
public delegate void FutureErrorCallback(Exception error);

Я хотел бы просто иметь возможность await в этом Будущем. Я считаю, что лучшим способом продвижения вперед было бы преобразование его в Task<T>. Но как бы вы поступили именно так? Подпись будет такой:

public static Task<T> ToAwaitable<T>(IFuture<T> future)
{
    //
}

Ответы [ 2 ]

3 голосов
/ 11 июля 2019

Вам не нужно оборачивать его в Task, все, что подходит GetAwaiter(), может быть await ed.

public class AwaitableFuture<T>
{
    private readonly IFuture<T> _future;

    public AwaitableFuture(IFuture<T> future)
    {
        _future = future;
    }

    public FutureAwaiter<T> GetAwaiter() => new FutureAwaiter<T>(_future);
}

FutureAwaiter<T> должен быть ожидающим для типа T. Объект является ожидающим T, если

  • Реализует INotifyCompletion;
  • Имеет свойство bool IsCompleted get;
  • У него есть метод T GetResult().

Таким образом, пример реализации будет:

public class FutureAwaiter<T> : INotifyCompletion
{
    private readonly IFuture<T> _future;

    public bool IsCompleted => _future.state == FutureState.Completed;

    public FutureAwaiter(IFuture<T> future)
    {
        _future = future;
    }

    public void OnCompleted(Action action) => _future.OnCompleted(x => action);

    public T GetResult() => _future.value;
}
1 голос
/ 11 июля 2019

В зависимости от деталей реализации будущего вы можете пойти так:

AsyncResult - это просто оболочка в Future, которая реализует правильный интерфейс и присоединяется к обратным вызовам.

internal class AsyncResult<T> : IAsyncResult
{
    private ManualResetEvent _manualResetEvent = new ManualResetEvent(false);

    public object AsyncState => null;

    public WaitHandle AsyncWaitHandle => _manualResetEvent;

    public bool CompletedSynchronously => false;

    public bool IsCompleted { get; private set; }

    public T Result { get; private set; }

    public Exception Error { get; private set; }

    public AsyncResult(IFuture<T> future)
    {
        future.OnSuccess(result =>
            {
                Result = result;
                IsCompleted = true;
                _manualResetEvent.Set();
            });

        future.OnError(() =>
        {
            Error = future.error;
            IsCompleted = true;
            _manualResetEvent.Set();
        });
    }
}

метод, который преобразует Future в Task, как вы хотели:

public static class FutureExtensions
{
    public static Task<T> ToAsync<T>(this IFuture<T> future)
    {
        var asyncResult = new AsyncResult<T>(future);
        var task = Task.Factory.FromAsync(asyncResult, x =>
        {
            var ar = (AsyncResult<T>)x;
            if (ar.Error != null)
            {
                throw new AggregateException("Task failed.", ar.Error);
            }

            return ar.Result;
        });

        return task;
    }
}

и пример использования:

internal static class Program
{
    public static async Task Main()
    {
        var future = new Future(success: true);

        var result = await future.ToAsync();

        Console.WriteLine(result);

        var future2 = new Future(success: false);

        try
        {
            var result2 = await future2.ToAsync();
        }
        catch (AggregateException e)
        {
            Console.WriteLine(e.InnerException.Message);
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...