Spaces in identifier names in C#


While I’m a fan of descriptive member names for testcase classes and test methods, there wasn’t a great way to create readable text.  Text in code editors is almost universally monospace, which reads very well for languages with lots of syntax.  But when it comes to stringing several words together to form a phrase, things get ugly.  Here’s a test I found in the StructureMap source code:

Or, let’s try that one converted to PascalCase:

As I see it, both of these options aren’t optimal.  It’s still work for me to read them, as each style is still only geared towards smallish groups of words.  But they don’t gel well to how people actually read, which is by groups of words, by scanning.  These naming styles simply don’t scan well, and to be honest, give me a headache after awhile.

While I’d rather not re-train myself to read with underscores, we can however do this:

Yes, this compiles.

Yes, it runs, in TD.NET and nunit-console.

Yes, it’s completely compliant to the ECMA C# language specification.   It still suffers from the same monospace issues, that monospace text is still more difficult to read than non-monospace text.  But I do find it visually less distracting.  Here’s how it’s done:

AutoHotKey fun

First, you’ll need to use either a VS Macro or AutoHotKey to help.  I use AutoHotKey to switch into a “Test Naming Mode”, borrowed from JP Boodhoo’s great start on the subject.  Mine is a little different at this point, as I have my R# template shortcut keys for generating BDD-style tests also kick off my AutoHotKey script.  “spec+TAB” creates my whole context base class, plus AutoHotKey listens to this same keystroke combination.  Otherwise, I can still use the same “Ctrl+Shift+U” keystroke to switch in and out of the test naming mode.

Somewhere along the line, you’ll see a method that either sends an underscore or a space to the target application:

;==========================
;Handle SPACE press
;==========================
$Space::
  if (IsInTestNamingMode) {
    Send, _
  } else {
    Send, {Space}
  } 

That first part, “Send, _” is what we’ll need to change.  Once we have our AutoHotKey script up and going, we can change it to send a very different character, a Unicode character instead of the underscore.

Trolling through Unicode

We can’t pick just any Unicode character of course.  First, it needs to be a valid C# identifier.  To find what a valid C# identifier is, we can reference the ECMA C# language specification, specifically section A.1.6, “Identifiers”.  In that section, it details what exactly a valid identifier can look like, described as the set of valid Unicode characters.  It’s much more than your traditional ASCII characters as well.

What I needed to find was a valid character for an identifier that looked like a space for the font I used.  There, it was simply a matter of trial and error going through the various categories allowed by the C# specification.  Some characters worked for some fonts and sizes, others did not.  Finally, I settled on one that worked for me at Consolas 10pt and 14pt, the font and size I use the most: UTF-16 0x200E, or “Left-to-right mark”.  I have zero idea what this character means, nor do I care, other than it’s a valid identifier character and it looks like a space in VS 2008 and most other text editors (and Word, Notepad2, Notepad++, etc.).

Once I found the right character, which I tested by copying and pasting into the editor, I was ready to change my AutoHotKey script to use the Unicode character instead of the underscore.

Sending Unicode through AutoHotKey

Unfortunately, AutoHotKey does not support sending Unicode characters very well out of the box.  From this forum post however, I found a great solution.  This code added to my AutoHotKey script:

EncodeInteger( p_value, p_size, p_address, p_offset )
{
   loop, %p_size%
      DllCall( "RtlFillMemory"
         , "uint", p_address+p_offset+A_Index-1
         , "uint", 1
         , "uchar", ( p_value >> ( 8*( A_Index-1 ) ) ) & 0xFF )
}

SendInputU( p_text )
{
   StringLen, len, p_text

   INPUT_size = 28
   
   event_count := ( len//4 )*2
   VarSetCapacity( events, INPUT_size*event_count, 0 )

   loop, % event_count//2
   {
      StringMid, code, p_text, ( A_Index-1 )*4+1, 4
      
      base := ( ( A_Index-1 )*2 )*INPUT_size+4
         EncodeInteger( 1, 4, &events, base-4 )
         EncodeInteger( "0x" code, 2, &events, base+2 )
         EncodeInteger( 4, 4, &events, base+4 ) ; KEYEVENTF_UNICODE

      base += INPUT_size
         EncodeInteger( 1, 4, &events, base-4 )
         EncodeInteger( "0x" code, 2, &events, base+2 )
         EncodeInteger( 2|4, 4, &events, base+4 ) ; KEYEVENTF_KEYUP|KEYEVENTF_UNICODE
   }
   
   result := DllCall( "SendInput", "uint", event_count, "uint", &events, "int", INPUT_size )
   if ( ErrorLevel or result < event_count )
   {
      MsgBox, [SendInput] failed: EL = %ErrorLevel% ~ %result% of %event_count%
      return, false
   }
   
   return, true
}

Allowed me to call one method to send the right Unicode value to my editor:

$Space::
  if (IsInTestNamingMode) {
    SendInputU("200E")
  } else {
    Send, {Space}
  }

Instead of sending an underscore character, I send the Unicode “200E” value, which happens to look just like a space.  Presto chango, my code now looks like it has spaces, but is still able to compile and run without skipping a beat.

Some major caveats

I’ve only played with this for a day or so, so I don’t know all the ramifications of going with these quasi-spaces in test class and method names.  Some things I’ve noticed so far:

  • R# Smart completion works some of the time (V4.5)
  • I don’t care

  • Test names are output without spaces or underscores
  • Could be a problem, maybe I just need to find a better space substitute

  • Anyone that wants to write test or method names like this NEEDS to have a macro or AutoHotKey script to convert spaces to the special character.  I haven’t found a way to easily, directly input this character inside the IDE.

This type of technique should not be used in any production, public API.  I’m targeting simply my test method and class names, which tend to get rather long and don’t have RSpec’s advantage of being able to use string literals.

It’s weird, but it works.

Validation in a DDD world