将回调对象转换为异步任务



我有一个接口,看起来像这样:

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

你不需要把它包装成一个Task,任何有合适的GetAwaiter()方法的东西都可以await编辑。

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获得财产;
  • 它有一个T GetResult()方法。

因此,示例实现将是:

public class FutureAwaiter<T> : INotifyCompletion
{
    private readonly IFuture<T> _future;
    public bool IsCompleted => 
        _future.state == FutureState.Success || _future.state == FutureState.Error;
    public FutureAwaiter(IFuture<T> future)
    {
        _future = future;
    }
    public void OnCompleted(Action action) => _future.OnComplete(_ => action());
    public T GetResult() => 
        _future.state == FutureState.Error ? _future.value : throw _future.error;
}

编辑:听从拉法尔的建议,我添加了错误抛出。他建议扩展方法也是正确的,这样您就可以直接await IFuture<T>

public static FutureAwaiter<T> GetAwaiter<T>(this IFuture<T> future) =>
    new FutureAwaiter<T>(future);
async Task PrintFuture<T>(IFuture<T> future)
{
    Console.WriteLine("Awaiting a future...");
    T result = await future;
    Console.WriteLine($"Future yielded {result}.");
}
<</div> div class="one_answers">

根据未来的实现细节,您可以像这样:

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

最新更新