Register | Login

Stacking Code

public interface IBlog { string Dump(Stream consciousness); }

Readable Code with Extension Methods

Tuesday, 18 September, 2012 @ 7:32 AM < Adam Boddington
Tags: C#

UPDATE: @SimonCropp just tweeted a link to his FluentDateTime library which handles some of the extensions below. It's even available via NuGet. Nice one.

One of the ways I like to keep my code readable and expressive is around anything to do with the DateTime and TimeSpan classes. Using DateTime or TimeSpan directly can be a little hard to comprehend when reading later. Here's an example. Say we have an NHibernate Linq query retrieving all widgets that are less than a day old.

var widgets = session.Query<Widget>()
    .Where(w => w.CreatedOn > DateTime.Now.AddDays(-1));

Forget the Linq in this example, the bit I find hard to read is the date we're comparing with.

w.CreatedOn > DateTime.Now.AddDays(-1)

The code isn't immediately communicating its intent. It's the sort of code that really begs for a quick comment.

var widgets = session.Query<Widget>()
    // Get widgets that are less than a day old.
    .Where(w => w.CreatedOn > DateTime.Now.AddDays(-1));

The problem with adding a comment however is that the code is now repeated in English. Changing one now requires a change to the other. If these ever get out of sync chaos will abound, cats and dogs will rain from the sky, or worse, the technical lead may get a headache. Keep it DRY (yes, I think most comments violate DRY) and readable. Enter extension methods and a neat little trick from Ruby on Rails.

w.CreatedOn > 1.Day().Ago()

How do we achieve such awesomeness? It's surprisingly easy. First we create an extension method that gives us the time span we care about.

public static class TimeSpanHelper
    public static TimeSpan Days(this int days)
        return TimeSpan.FromDays(days);

We only care about one day in this particular example and it's not hard to cater for the singlular form.

public static TimeSpan Day(this int day)
    return Days(day);

Next, we create something that can cast our time span into the past.

public static class DateTimeHelper
    public static DateTime Ago(this TimeSpan timeSpan)
        return DateTime.Now.Subtract(timeSpan);

And the future (while we're here).

public static DateTime FromNow(this TimeSpan timeSpan)
    return DateTime.Now.Add(timeSpan);

Now we can write the Linq query like so.

var widgets = session.Query<Widget>()
    .Where(w => w.CreatedOn > 1.Day().Ago());

Much clearer. I don't think that code needs a comment. Nothing to get out of sync either. Your technical lead may still get a headache from the extension methods, but we can't have it all. ;)

Of course TimeSpanHelper can be extended to cater for seconds, minutes, hours, etc. Here's one more example. Say we wanted to find the time half a week from now. There's a few ways to do that.

(3.Days() + 12.Hours()).FromNow();

The last two require extending double instead of int, like this.

public static TimeSpan Weeks(this double weeks)
    return TimeSpan.FromDays(weeks * 7);

The double extensions can be handy at times, but I prefer to use int whenever I can. I think it's a little more readable.

Before I go, what do you think of this statement?


I'll leave it up to you to implement.

There are 0 comments.


Leave a Comment

Please register or login to leave a comment.

Visual Studio 2010 and Vim

Roy Osherove's TDD Kata 1

Visual Studio 2010 and Vim

Roy Osherove's TDD Kata 1

browse with Pivot



Building Neno

Recent Posts

Codility Nitrogenium Challenge
OS X Lock
HACT '13
Codility Challenges
Priority Queue


Architecture (13)
Brisbane Flood (1)
Building Neno (38)
C# (4)
Challenges (3)
Collections (1)
Communicator (1)
Concurrency Control (2)
Configuration (1)
CSS (5)
DataAnnotations (2)
Database (1)
DotNetOpenAuth (2)
Entity Framework (1)
FluentNHibernate (2)
Inversion of Control (5)
JavaScript (1)
jQuery (4)
Kata (2)
Linq (7)
Markdown (4)
Mercurial (5)
NHibernate (20)
Ninject (2)
OpenID (3)
OS X (1)
Pivot (6)
PowerShell (8)
Prettify (2)
RSS (1)
Spring (3)
SQL Server (5)
T-SQL (2)
Validation (2)
Vim (1)
Visual Studio (2)
Windows Forms (3)
Windows Service (1)


Powered by Neno, ASP.NET MVC, NHibernate, and small furry mammals. Copyright 2010 - 2011 Adam Boddington.
Version 1.0 Alpha (d9e7e4b68c07), Build Date Sunday, 30 January, 2011 @ 11:37 AM