Как сформировать коллекцию TaskCompletionSource <T>и сохранить безопасность типов - PullRequest
3 голосов
/ 29 марта 2019

У меня есть несколько асинхронных методов:

// not ideal
private TaskCompletionSource<A> _tcsA;
private TaskCompletionSource<A> _tcsB;
private TaskCompletionSource<A> _tcsC;
...

public Task<A> GetAAsync() {
  _currentTask = TaskType.A;
  _tcsA = new TaskCompletionSource<A>();
  // some complex non-sync task, using http/events that ends with Complete();
  // QueueRequest?.Invoke(this, taskInfo); // raise request -- this does not return
  return _tcsA.Task;
}

public Task<B> GetBAsync() {
  _currentTask = TaskType.B;
  _tcsB = new TaskCompletionSource<B>();
  // some complex non-sync task, using http/events that ends with Complete();
  // QueueRequest?.Invoke(this, taskInfo); // raise request -- this does not return
  return _tcsB.Task;
}

public Task<C> GetCAsync() {
  _currentTask = TaskType.C;
  _tcsC = new TaskCompletionSource<C>();
  // some complex non-sync task, using http/events that ends with Complete();
  // QueueRequest?.Invoke(this, taskInfo); // raise request -- this does not return
  return _tcsC.Task;
}

// called by an external source, a http listener / processor
// this should complete the async call by the client and return results
public void Complete(Result result) {
  switch (_currentTask) {
    case TaskType.A:
      _tcsA.SetResult(new A());
      break;
    case TaskType.B:
      _tcsB.SetResult(new B());
      break;
    case TaskType.C:
      _tcsC.SetResult(new C());
      break;
  }

  _currentTask = TaskType.None;
}

Выше приведен полупсевдокод для простоты.Я называю один из методов, например, так:

A a = await service.GetAAsync();

Теперь проблема в том, что TaskCompletionSource<T> является общим, если у меня есть 100 методов таким образом, мне придется создатьпеременная для каждого возвращаемого типа.Но поскольку может быть вызван только один метод, было бы неплохо использовать один TaskCompletionSource, но не вводить его в object (TaskCompletionSource<object>).

Я не хочу этого делать:

object a = await service.GetAAsync();

Потому что это потребует приведения клиентом.Поэтому лучшим решением было бы иметь один TaskCompletionSource, но набрать его как-нибудь.Или же иметь возможность иметь словарь TaskCompletionSource.И то, и другое кажется мне невозможным.

Как мне решить эту проблему?

Обновление:

Чтобы узнать о моей ситуации, взгляните на: Синхронный переноскод в асинхронном режиме ожидания в отключенном сценарии

1 Ответ

3 голосов
/ 29 марта 2019

Я не хочу делать object a = await service.GetAAsync(), потому что это потребует приведения клиентом.

Не обязательно. Сохраните ту же подпись, но добавьте вызов к ContinueWith для приведения к соответствующему типу:

public Task<A> GetAAsync() {
  _currentTask = TaskType.A;
  _tcs = new TaskCompletionSource<object>();
  // some complex task that ends with Complete();
  return _tcs.Task.ContinueWith(t => (A)t.Result);
}

Или вы можете сделать то же самое, используя async / await:

public async Task<A> GetAAsync() {
  _currentTask = TaskType.A;
  _tcs = new TaskCompletionSource<object>();
  // some complex task that ends with Complete();
  return (A)await _tcs.Task;
}
...