Fun With Anonymous Methods

A few months ago I wrote some code that really made me appreciate the new language feature in C#: Anonymous Methods. I decided to turn it into a sample application that creates a web request to www.google.com and downloads it when you push a button... asynchronously (that means the UI does not hang while the application is downloading). Prior to C# 2.0, doing a web request and download the content asynchronously was several hundred lines of sloppy spaghetti code: you need to implement callbacks, cleanup, error handling, state maintenance, etc. A lot of developers, myself included, would “cheat” and wrap synchronous calls in a thread to fake asynchronous requests (this is not ideal for performance).

The beauty of anonymous methods is that the compiler’s syntactic sugar handles a lot of the aforementioned for the developer very elegantly in the background.

Here’s what a fully asynchronous download looks like now, with error handling and cleanup and all (5 anonymous methods in total):

HttpWebRequest req = WebRequest.Create("http://www.google.com") as HttpWebRequest;
MemoryStream memStream = new MemoryStream();
HttpWebResponse resp = null;

// anonymous method to handle cleanup
EventHandler Cleanup = (s, ev) =>
    {
        using (resp)
        {
        }
        resp = null;
        using (memStream)
        {
        }
        memStream = null;
    };

// anonymous method to handle errors
EventHandler Error = (s, ev) =>
{
    MessageBox.Show((s as Exception).Message);
    Cleanup(null, null);
};

req.BeginGetResponse(responseResult =>
    { // anonymous method that waits for a response from the web server
        Thread.Sleep(1000); // for asynchronous testing
        try
        {
            resp = req.EndGetResponse(responseResult) as HttpWebResponse;
            Stream responseStream = resp.GetResponseStream();
            byte[] buffer = new byte[1024];
            AsyncCallback rc = null;
            rc = readResult =>
                { // anonymous method that waits to read a some data from the web server
                    Thread.Sleep(1000); // for asynchronous testing
                    try
                    {
                        int read = responseStream.EndRead(readResult);
                        if (read > 0)
                        {
                            memStream.Write(buffer, 0, read);
                            responseStream.BeginRead(buffer, 0, buffer.Length, rc, null);
                        }
                        else
                        {
                            // We finished downloading! Invoke the UI thread to print the data!

                            // anonymous method to handle completion
                            EventHandler Done = (s, ev) =>
                            {
                                memStream.Seek(0, SeekOrigin.Begin);
                                string blob = new StreamReader(memStream).ReadToEnd();
                                Cleanup(null, null);
                                MessageBox.Show(blob);
                            };

                            Invoke(Done);
                        }
                    }
                    catch (Exception ex)
                    {
                        Invoke(Error, ex);
                    }
                };
            responseStream.BeginRead(buffer, 0, buffer.Length, rc, null);
        }
        catch (Exception ex)
        {
            Invoke(Error, ex);
        }
    },
    null);

1 comments:

Joshua Smith said...

Thanks for helpful review! For business solutions it is the right way to get outsourcing software development services.