Feeds:
Posts
Comments

Posts Tagged ‘Extension Methods’

ForEachLine

To aid in the development of my main application work, I often find myself writing lots of little C# apps to analyze diagnostic bread crumbs left behind by in-development code. Yeah, I said C# and not Ruby or Python or whatever. This is mainly because of the Linq facilities and what they offer for analyzing and projecting lists of data. Because these are C# apps and the bread crumbs typically take the form of files, I would constantly write code to this effect:

    var file = @"C:\logs\diag.log";
    if (File.Exists(file))
    {
      using (StreamReader reader = File.OpenText(file))
      {
        while (reader.Peek() > -1)
        {
          doSomething(reader.ReadLine());
        }
      }
    }

This really grows tiresome when all you really want to do is act on the content of the file. Ruby has a nice shortcut for this using the foreach class method on File (or IO).

    File.foreach("C:\\logs\\diag.log") { |line| doSomething(line) }

As it turns out, this sort of behavior is easy to add to C#:

    public static void ForEachLine(string file, Action action )
    {
      if (File.Exists(file))
      {
        using (StreamReader reader = File.OpenText(file))
        {
          while (reader.Peek() > -1)
          {
            action(reader.ReadLine());
          }
        }
      }
    }

which opens the door to calls like

    ForEachLine( @"C:\logs\diag.log", line => doSomething(line) );

Thats nice and everything, but I wanted to make an extension method out of this and put it on the File class (which would be the intuitive thing to do). The problem is that extension methods in C# as of 3.0 are only allowed to be instance methods. So I’m denied the File.ForEachLine() invocation. I don’t like the prospect of having to construct a FileInfo instance to call this since in order to do that I would have to write all the boilerplate code this call is intending to avoid in the first place.

There is, however, a lightweight object that is already being created in every one of these cases that actually winds up making sense as the target of this call.

the string.

If we stick our call from before in an extension class and change the first param to be the call receiver, we get this:

    public static class Extensions
    {
      public static void ForEachLine(this string file, Action action )
      {
        if (File.Exists(file))
        {
          using (StreamReader reader = File.OpenText(file))
          {
            while (reader.Peek() > -1)
            {
              action(reader.ReadLine());
            }
          }
        }
      }
    }

This might not look too different, but allows us the following syntax:

    var file = @"C:\logs\diag.log";
    file.ForEachLine( line => doSomething(line) );

or even

    @"C:\logs\diag.log".ForEachLine( line => doSomething(line) );

This is succinct, intuitive, and makes those little C# apps feel a little more Rubyish. That might even be a good thing.

Advertisements

Read Full Post »