I have been playing around with asynchronous programming lately and it
bothers me that the last parameter to the begin invoke pattern doesn't
have a name that everyone agrees on. The begin invoke pattern is the
asynchronous calling pattern were the BeginXxxx()
, such as
Stream.BeginRead()
, takes a set of parameters, a callback method, and
some, well, thing, at the end that is carried along with the asynchronous
operation that eventually finds it way into the IAsyncResult
. The
problem is what do we call that thing? In an informal survey of the methods in
the BCL that implement this pattern I have found a wide variation. Here is a
partial list in somewhat order of popularity,
- object
- stateObject
- state
- asyncState
- extraData
- data
There seems to be little agreement about what to call it. We could pick the
most prevalent but the name
object
occurs the most often because every
delegate gets a BeginInvoke()
method created for it and in this
automatically generated code the parameter is called object
. We can't standardize on object
because it is a reserved word in several languages so either it would be impossible to
specify or awkward (i.e. @object
in C#). What I would like is a
name that we can all use and we can all agree on.
To name the thing, we first must understand why it it is there at all. It exists to hold some state on behalf of the caller. Having the state object makes programming against the begin invoke pattern easier in languages that do not have anonymous methods that capture local variables. Consider the following program (which intentionally ignores errors because it is only an example),
conststring uriFormat = "http://messenger.services.live.com/users/{0}@apps.messenger.live.com/presence";conststring responsePattern = @"""statusText""\s*:\s*""(?<status>\w+).*""displayName""\s*:\s*""(?<name>\w+)""";staticvoid Main(string[] args) {var uri = string.Format(uriFormat, args[0]);var request = WebRequest.Create(uri);var result = request.BeginGetResponse(PresenceCallback, request); // Other interesting work.... result.AsyncWaitHandle.WaitOne(); } privatestaticvoid PresenceCallback(IAsyncResult result) {var request = result.AsyncState as WebRequest;var response = request.EndGetResponse(result);var s = new StreamReader(response.GetResponseStream()).ReadToEnd();var search = new Regex(responsePattern);var match = search.Match(s); if (match.Success) Console.WriteLine("{0} is {1}", match.Groups["name"].Captures[0].Value, match.Groups["status"].Captures[0].Value);else Console.WriteLine("Unexpected response"); }
This will get the online status of a Windows Live Messanger account given the
live ID account number. For example, passing 1afa695addc07e5 as an argument to
the above will tell whether or not I am online. In this case I am using last
parameter of the BeginGetResponse()
method to pass the request
itself. This then is cast back to WebRequest
in the
PresenceCallback()
method so I can call EndGetResponse()
to
retrieve the actual response. As far at the BeginGetResponse()
call
is concerned, this value is opaque. It ignores it completely and just supplies
it blindly in the IAsyncResult
. It makes no assumptions about the
data at all, it is just something the caller just wants carried around. If I was using
anonymous delegates in C# this would look a lot better as,
staticvoid Main(string[] args) {var uri = string.Format(uriFormat, args[0]);var request = WebRequest.Create(uri); request.BeginGetResponse(result => {var response = request.EndGetResponse(result);var s = new StreamReader(response.GetResponseStream()).ReadToEnd();var search = new Regex(responsePattern);var match = search.Match(s); if (match.Success) Console.WriteLine("{0} is {1}", match.Groups["name"].Captures[0].Value, match.Groups["status"].Captures[0].Value);else Console.WriteLine("Unexpected response"); } }, null); // Other interesting work.... result.AsyncWaitHandle.WaitOne(); }
Here the request local variable request
is captured
automatically by the C# compiler and placed into a compiler generated class as a
field. The compiler generated class also contains the code I supplied in the
lambda as an instance method. When I refer to response in the lambda the
reference is automatically translated into a field lookup in the generated
class. Since request
is already accessible in the lambda I don't
need the last parameter to carry anything useful so I pass null
.
Anonymous methods makes using callbacks much easier but since not all languages support anonymous methods or lambdas the BCL standardized on a method pattern for begin invoke that can easily be used by those languages. If the calling pattern did not have a caller state object then the work performed automatically by the C# compiler would have to be repeated manually by the programmer in these, then, second class languages. The .NET team did not want such languages to be second class citizen (especially since neither C# nor VB.NET supported anonymous methods initially) so they required the presence of the caller state object parameter.
Now we know why it is there, what to do we call it? I like state
because the parameter represents state the caller want's to preserve. I don't
like object
because it is a common reserved word. I don't likestateObject
because a symbol's type should not be repeated in the
name, we already know it is an object
by its type.
asyncState
is acceptable, especially since that is the name it is given
by IAsyncResult
, but it is a bit redundant, we already know, by
context, it is asynchronous state. Plus we should avoid abbreviation, like "async",
in symbol names (though it is very common, and asynchronous is very very long,
so not that bad). data
seems fine to me, it is a synonym to state,
but it is overused. extraData
I cannot, for the life of me, figure
out. Extra for whom? Extra as opposed to what? Unfortunately, this is the name
given to the parameter by IHttpAsyncHandler
(see, I told you "async"
was common). Its name tells me nothing about what I should do with it. It
is very unclear that this value should be mapped to AsyncState
in IAsyncResult
.
I propose we call it state
, with an acceptable variation ofasyncState
.
Now, if changing a parameter name was not a breaking change...
Trivia:
- The above uses the Live Services that you can find more about here.
- The IM presence service returns JSON which I parse using a fancy looking
Regex
instance. I recommend that production code useDataContractJsonSerializer()
instead.