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>
?
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
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With