5

As detailed in the TPL and Traditional .NET Framework Asynchronous Programming MSDN article and Stephen Toub's Tasks and the APM Pattern blog post, the TaskFactory.FromAsync() and TaskFactory<TResult>.FromAsync() methods can be used to adapt Begin*() and End*() APIs conforming to the APM pattern for use with the Task Parallel Library.

Below I will address only the TaskFactory<TResult>.FromAsync() overloads, as my question does not depend on whether the APM-conforming APIs result in a value.

There are three overloads that accept an IAsyncResult object, namely:

  • FromAsync(IAsyncResult, Func<IAsyncResult, TResult>)
  • FromAsync(IAsyncResult, Func<IAsyncResult, TResult>, TaskCreationOptions)
  • FromAsync(IAsyncResult, Func<IAsyncResult, TResult>, TaskCreationOptions, TaskScheduler)

I understand that these APIs are useful in scenarios where the Begin*() API method takes more than three parameters in addition to the AsyncCallback and state object (e.g. Too many arguments in BeginXXX for FromAsync?), or when the Begin*() API does not fully conform to the APM pattern because it does not take a state object parameter (e.g. How can I use the AsyncCTP with an TFS APM Method (Query.Begin/EndQuery)?).

What I don't understand is why the remaining overloads (e.g. FromAsync(Func<AsyncCallback, Object, IAsyncResult>, Func<IAsyncResult, TResult>, Object)) all require a state object to be supplied. This state object is passed to the Begin*() API, but in the APM pattern, the state object is for private use by the caller. So, it shouldn't matter what state object is passed to the Begin*() API by FromAsync().

The MSDN article linked above has a section titled "Providing Custom State Data" which recommends capturing any required state in a continuation delegate and passing null for the state object to FromAsync(). However, this does not explain why the FromAsync() methods require a state object to be given.

Why is a state object required to be passed when it is not accessible to the returned Task<TResult>?

Community
  • 1
  • 1
Daniel Trebbien
  • 38,421
  • 18
  • 121
  • 193

1 Answers1

5

Well, If you follow the source code you can see that the state can indeed be accessed from the Task instance using the (not so surprisingly named) Task.AsyncState:

var task = Task.Factory.FromAsync<TResult>(...);
object state = task.AsyncState

This property holds the state of the asynchronous operation in other scenarios as well, like Task.Factory.StartNew:

var task = Task.Factory.StartNew(_ => { }, "bar");
Console.WriteLine(task.AsyncState); // prints "bar"

So, since the state is accessible, it's expected to be able to pass a state in as a parameter and doubling the API once with the state parameter and once without doesn't sound like the better option. Especially when you can just pass in null.

i3arnon
  • 113,022
  • 33
  • 324
  • 344
  • Ah! Did not know about `Task.AsyncState`. I presume that it was added specifically for this purpose? Are there any other uses of `Task.AsyncState`? – Daniel Trebbien Aug 01 '15 at 17:38
  • 1
    @DanielTrebbien I doubt it. It's a general concept used in performance-heavy scenarios. It's also used when you pass in a state in `Task.Factory.StartNew` for example. – i3arnon Aug 01 '15 at 17:42
  • The APM contract also has a `state` argument. It's only logical that this API would account for that. – Paulo Morgado Aug 02 '15 at 19:54
  • @PauloMorgado of course. That's what the question is about. – i3arnon Aug 02 '15 at 19:58