创建一个异步方法,包装订阅并在总线上发布

时间:2014-02-15 13:08:51

标签: c# asynchronous .net-4.5 publish-subscribe c#-5.0

我有一种实现此接口的总线:

public interface IBus
{
    void Publish<T>(T t);
    void Subscribe<T>(Guid subscriptionId, Action<T> action);
    void Unsubscribe<T>(Guid subscriptionId);
}

以下是我如何使用它的示例:

public void PrintName()
{
    IBus bus = new Bus();
    var id = Guid.NewGuid();
    bus.Subscribe<ReplyUserName>(id, replyUserName =>
    {
        bus.Unsubscribe<ReplyUserName>(id);
        Console.WriteLine(replyUserName.UserName);
    });

    Bus.Publish(new RequestUserName());
}

以下是RequestUserName和ReplyUserName类:

public class RequestUserName {}

public class ReplyUserName
{
    public string UserName { get; set; }
}

但是我想编写一个扩展方法,用async包装它:

public static class BusExtension
{
    public static async Task<TResult> Request<TRequest, TResult>(this IBus bus, TRequest request)
    {
        // TODO...
    }
}

这样我就可以用这样的方式编写前面的代码:

public async void PrintName()
{
    IBus bus = new Bus();
    var replyUserName = await bus.Request<RequestUserName, ReplyUserName>(new RequestUserName());
    Console.WriteLine(replyUserName.UserName);
}

我应该写什么而不是TODO?

2 个答案:

答案 0 :(得分:6)

您可以将TaskCompletionSource<T> wrap anything用于await兼容方法。

public static Task<TResult> Request<TRequest, TResult>(this IBus bus, TRequest request)
{
  var tcs = new TaskCompletionSource<TResult>();
  var id = Guid.NewGuid();
  bus.Subscribe<TResult>(id, result =>
  {
    bus.Unsubscribe<TResult>(id);
    tcs.TrySetResult(result);
  });
  bus.Publish(request);
  return tcs.Task;
}

但请注意,您应该ensure that the task is completed。如果总线有可能无法响应请求,您应该有一个计时器或某个错误TaskCompletionSource的东西。

答案 1 :(得分:5)

您可以按如下方式实现:

var taskCompletionSource = new TaskCompletionSource<TResult>();
bus.Subscribe<TResult>(id, result =>
{
    bus.Unsubscribe<TResult>(id);
    taskCompletionSource.SetResult(result);
});
bus.Publish(request);
return taskCompletionSource.Task;

您可能还想查看Reactive Extensions(Rx),因为您的IBus界面看起来与ISubject界面(http://msdn.microsoft.com/en-us/library/hh211669.aspx)类似。 Reactive Extensions库已经提供了类似于您尝试实现的方便的扩展方法。