Разница между Task.Factory.FromAsync и BeginX/EndX?

У меня очень похожий код при использовании стандартных методов BeginRead и EndRead из TcpClient и использования Task.Factory.FromAsync.

Вот несколько примеров. Ошибка обработки кода не отображается.

Task.Factory.FromAsync:

private void Read(State state)
{
    Task<int> read = Task<int>.Factory.FromAsync(state.Stream.BeginRead, state.Stream.EndRead, state.Bytes, state.BytesRead, state.Bytes.Length - state.BytesRead, state, TaskCreationOptions.AttachedToParent);

    read.ContinueWith(FinishRead);
}

private void FinishRead(Task<int> read)
{
    State state = (State)read.AsyncState;

    state.BytesRead += read.Result;
}

Стандартное использование обратных вызовов с BeginRead и EndRead:

private void Read(State state)
{
    client.BeginRead(state.Bytes, state.BytesRead, state.Bytes.Length - state.Bytes.Read, FinishRead, state);
}

private void FinishRead(IAsyncResult async)
{
    State state = (State)async.AsyncState;

    state.BytesRead += state.Stream.EndRead(async);
}

Обе эти работы прекрасны, но мне любопытно их отличия. Строки кода для обоих в значительной степени эквивалентны, и оба они, похоже, выполняют ту же самую функцию и имеют одинаковую эффективность. Какой из них предпочтительнее? Что бы вы предпочли в производственном коде?

Ответ 1

Я бы скорее посмотрел код Task<T> на основе:

  • Он обеспечивает композицию более легко; например, достаточно легко написать метод, который берет коллекцию задач Task<T> и возвращает другую задачу, которая представляет собой вердикт большинства этих задач. Кроме того, вы можете подождать, пока не завершится какая-либо одна из заданий, и т.д.
  • Он обеспечивает более гибкое планирование того, где выполняется продолжение.
  • Он позволяет вернуть задачу с безопасностью типа и намного больше информации, чем несколько анемичный тип IAsyncResult, возвращаемый BeginRead.
  • Проще задавать обработку ошибок и отмену задач, чем использование модели Begin/End.
  • Task<T> улучшает поддержку языков в С# 5 с помощью async/await - если ваша кодовая база уже использует Task<T> повсеместно, будет намного проще воспользоваться этим

В основном в современном коде, запущенном на .NET 4, Task<T> - это идиоматический способ представления текущей задачи. Это гораздо более богатая среда для работы, чем раньше, и я бы обнял ее, если у вас есть такая возможность. Очевидно, что если вы используете .NET 3.5 или ранее, жизнь немного сложнее, но я предполагаю, что, когда вы задаете вопрос, Task<T> - это вариант...