Calling Control

1

April 12, 2011 by Alistair Deneys

Several years ago I remember watching a dnrTV (dot net rocks TV) episode featuring Venkat Subramaniam discussing Ruby like features of .NET 2 and C# 2. You can catch the episode at http://www.dnrtv.com/default.aspx?showNum=41. One programming technique that stuck in my head from that episode was to do with passing control inside a method back to the calling code to allow it to fill in the specific detail.

When implementing a method of a class, if you find yourself repeating the same code in several methods that code should be extracted as a separate method and called from each of the other methods requiring it. This means less code and this common logic is in a single location making maintenance much easier. This is fine when the common code doesn’t change, but what if you have small changes in the middle of the common code? For example you might have a bunch of error checking and resource allocation going on before you get to the specifics of the method and the only thing changing between the methods are a few lines of code that might also be nested several curly braces deep.

In Ruby the yield keyword works very different to what we’re used to in C#. In C# yield is used as a convenient way of creating an IEnumerable.

C#

public IEnumerable<int> GetNums()
{
  for (var i = 0; i < 5; i++)
    yield return i;
}

Rather than creating an output array or list, adding my elements to it then returning in the appropriate format, I can make use of the yield return statement to output all the values I want from the method and I’ll get back an IEnumerable<int> that I can iterate over. It’s less code and cleaner.

In Ruby the yield keyword is used to pass control back to the calling code so it can provide specific detail or use the data passed through yield.

Ruby

def doSomething
  for num in 0..5
    yield num
  end
end

doSomething { | data |
  puts data
}

In the above example I’m calling the doSomething method which calls the code block in the calling code each time the yield statement is called inside the method.

This is quite a nice technique. I have at times come across occurrences where most of the method I was working on was the same as another, except for a few lines of code in the middle.

In the dnrTV episode Venkat produced the same result in C# using delegates. Delegates are basically method pointers which will invoke a different method and allow you to pass methods into other code, a little like a functional language. Let’s take a look at how we could do the same in C# using anonymous delegates. For the following example try and imagine we’re trying to get typed data from a data store. The GetString method will return the typed data. This method will use the DataFromStore method which will get the raw data and call back to the GetString method to provide the formatting of the data.

C#

class ControlWithDelegate
{
  public delegate string GetData(object data);

  public string GetString(string key)
  {
    // get data from data store
    return DataFromStore(key, delegate(object data)
    {
      return (string)data;
    });
  }

  public string DataFromStore(string key, GetData formatter)
  {
    // get data from data store
    object data = "data from store using key";
    return formatter(data);
  }
}

The reason this is an anonymous delegate is because it’s declared inline. We haven’t declared a delegate which is another named method to use.

The GetString method calls the DataFromStore method passing the 2nd parameters as an anonymous delegate. Inside the DataFromStore method we can use that parameter as a method. This causes the code inside the anonymous delegate to execute. The result is then returned from DataFromStore to GetString which then returns the data to it’s caller.

This is a very elegant way of using the same technique as we can see in the Ruby example above. It allows us to pass control back to the calling code momentarily.

But I would like to make it smaller. I don’t like that I need to define the delegate signature. Well with the introduction of the Func delegates in C# 3, we no longer have to. Let’s rewrite the above example using Func delegates instead of our own defined delegate.

C#

class ControlWithFunc
{
  public string GetString(string key)
  {
    // get data from data store
    return DataFromStore(key, delegate(object data)
    {
      return (string)data;
    });
  }

  public string DataFromStore(string key, Func<object, string> formatter)
  {
    // get data from data store
    object data = "data from store using key";
    return formatter(data);
  }
}

I know it’s not much, but I like not having to define a delegate type which might only be used by a handful of methods.

Lambda expressions are a compact way of declaring anonymous delegates. I can compact the code slightly further by using a lambda expression instead of declaring the anonymous delegate.

C#

public string GetString(string key)
{
  // get data from data store
  return DataFromStore(key, data =>
  {
    return (string)data;
  });
}

public string DataFromStore(string key, Func<object, string> formatter)
{
  // get data from data store
  object data = "data from store using key";
  return formatter(data);
}

Just as a point of reference, here’s the equivalent Ruby code for the above C# sample.

Ruby

class MyClass
  def getString(key)
    dataFromStore(key) { |data| return data }
  end

  def dataFromStore(key)
    data = yield "data from store using key"
    return data
  end
end

Now let me give you a more real world example. The specific scenario I was recently working on was to do with creating a class to deal with fetching documents over HTTP. I needed to be able to fetch both text and binary documents. When it came to implement my GetTextDocument and GetBinaryDocument methods I found most of the method would be the same between those two. In both cases I need to created an HttpWebRequest, get the stream and clean up when done. The piece that would differ would be how I read the data from the stream and the return type. This lead me to exploring using the above technique to allow me to write less code.

First I’ll start with the common functionality to be used by both methods.

C#

private T GetDocument<T>(string url, Func<Stream, long, T> impl)
{
  if (!string.IsNullOrEmpty(url))
  {
    var request = HttpWebRequest.Create(url);
    using (var response = request.GetResponse())
    {
      using (var stream = response.GetResponseStream())
      {
        T output = impl(stream, response.ContentLength);
        return output;
      }
    }
  }
  else
    return default(T);
}

Note how I’m using generics to allow the calling code to define the type of data that will be returned. Now I’ll implement the GetTextDocument method.

C#

public string GetTextDocument(string url)
{
  return GetDocument(url, (stream, length) =>
  {
    var streamReader = new StreamReader(stream);
    return streamReader.ReadToEnd();
  });
}

And then the GetBinaryDocument method.

C#

return GetDocument(url, (stream, length) =>
{
  var chunkSize = 1024f;
  if (chunkSize > length)
    chunkSize = length;

  var read = 0;
  var offset = 0;

  var output = new byte[length];

  while((read = stream.Read(output, offset, (int)chunkSize)) > 0)
  {
    offset += read;

    if((offset + chunkSize) > length)
      chunkSize = length - offset;
  }

  return output;
});

And there you have it. An easy way to allow passing control of a method back to it’s caller to allow the caller to fill in specifics of a generic implementation.

Advertisements

One thought on “Calling Control

  1. […] momentarily back to the calling code and have it do the casting. I’ve shown previously how to pass control back to the calling code, and this approach is basically the […]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Categories

The views expressed on this blog are solely my own and do not necessarily reflect the views of my employer.
%d bloggers like this: