Code Generation with LINQPad 4

Today I encountered a task at work that offered the prospect of some pretty dull development work–code that needed to be written that was almost the same in multiple cases (but not quite).  It seemed like work that could benefit from the use of T4 templates, but quickly became frustrated by the process of setting up and debugging a template.  The interleaving of angle bracket markup with code was never fun in XML, and T4 templates began to resemble that very quickly.

So after abandoning the T4 template approach, I fired up LINQPad to see if I could accomplish my goal in that.  As it turned out, writing a small C# program in LINQPad for code generation was a lot easier.  I just needed to remember two key things about string substitution in verbatim string literals.  Here they are:

  1. Curly brackets need to be escaped.  So “{” should be “{{” and “}” should be “}}”.  Not doing this will result in a FormatException.
  2. Double quotes need to be escaped.  So ” should be “”.

Beyond that, it was a matter of writing a code template inside a verbatim string literal (@”<code template goes here>”) with format items where needed ({0},{1},…).

I’ve made a code sample available as GitHub gist here.  So far, I’ve used this technique to generate nearly 20 files in a fraction of the time it would have taken to write them manually.  Very little manual tweaking of the files was needed after generation, which left more time to test the generated code in real scenarios.

Infinity, Epsilon and NaN

I inadvertently learned some new things about the .NET double struct while refactoring some code over the past couple of days.  A large set of test data we received recently revealed that we weren’t correctly handling the following edge cases of body mass index (BMI) calculation.

  • Edge case 1: a height of zero inches
  • Edge case 2: a weight of zero pounds and a height of zero inches
  • Edge case 3: a weight of zero pounds and a non-zero height

Incorrectly handling the first case resulted in an overflow exception in our code, but only because it tried to pass the result of a BMI calculation that returned the constant PositiveInfinity into another method.  To handle this condition more gracefully (since a height of zero inches and/or a weight of zero pounds are considered valid inputs), a check for PositiveInfinity is needed.  The .NET framework provides the static method IsPositiveInfinity for such comparisons.

Incorrect handling of the second case caused an error as well.  But this time the BMI calculation returned the constant NaN.  Like PositiveInfinity, NaN has a static method to check for it (IsNaN).

Enough time has gone by (and enough code changes have taken place) since I first started this post back in September that I don’t recall precisely what the original code did in the third case.  Under normal circumstances the BMI calculation would return zero, but our implementation returns the NaN constant if the BMI (or inputs to it) falls outside what are considered valid ranges.