Tuesday, August 31, 2010

HyperJS Episode 2 - Attack of the Accessors

JavaScript in C# IS NOT ExpandoObject

Posts in the HyperJS Series

Last time, I discussed the first step on the road to JavaScript in C# in my HyperJS Episode 1 - The Phantom Project post. It covers the original intent of my weekend prototyping and the result: HyperDictionary - An implementation of IDictionary<string, object> with "inheritance," overrides, and extensions. All of the projects I'll talk about in this series are available on (my first) GitHub repos: http://github.com/tchype.

In part 2, I'll be covering how Rob Conery's post that I'll incorrectly label "Trying to implement Ruby Hashes In C# But C# Leaves A Lot To Be Desired In That Regard (I know, Oren, Use a Dynamic Language If I want Dynamism)" was an influence once I realized I was headed down this path, how I really just wanted a simple feature, and how I had to build it myself. Then, once I did it (very easily), I started down a weird and fun path of seeing if I could make JavaScript in C#.

HyperJS Part 2: HyperDynamo

Now that I had created a HyperDictionary, I was wondering how hard it would be to allow a Dictionary to be populated by a data source using the key/value pairs defined in the data store, but then accessed via the dot-notation of a property name. The thought here is that although a data-store-driven key/value pair allows you to arbitrarily add a key and value, typically you have to write code to use it anyway. With recordsets and dictionaries, you typically create constants somewhere in your code (because, after all, we all want to keep it DRY, right?) and then have this kind of annoying code all over your project:

public const FIRST_NAME_KEY = "first_name";
public const LAST_NAME_KEY = "last_name";
...

// Read from DB into an object
var user = new Person()
 { 
   FirstName = record[FIRST_NAME_KEY], 
   LastName = record[LAST_NAME_KEY],
   // ... more properties ... 
};
...

//Set the user's first name into a ViewData key
ViewData["user_first_name"] = user.FirstName;

Just to be clear: I don't actually code directly against the recordset in the view or controller...I like ViewModels and DTO objects and exception handling; this is just an obscene example that illustrates the point of using constants to reference field and key names.

After seeing Rob Conery's post on doing Ruby-esque things in C# (and having done some in MVC and side Rails projects), I wondered if it would be possible to do something more like JavaScript...wouldn't it be nice if you had the option to easily populate an object's properties by looping through the key names and setting their values (ignoring the tight coupling and issues with valid property name validation):

dynamic user = new ExpandoObject()
foreach (string key in myDictionary.Keys)
{
  user[key] = myDictionary[key];  //Actually, this is an Exception...
}

Even when we use constants for key names, we are essentially compiling the key names in for those keys we know and care about and write code against. And then, in cases of quick internal apps, prototypes, or just for eliminating the need for the stupid constants, we could just reference the properties we care about against the dynamic binding:

ViewData["user_first_name"] = user.first_name

ExpandoObject Is Not Good Enough

Although ExpandoObject is really cool, it relies purely on dot-notation property setters and getters (no indexers), so this doesn't actually serve the purpose. So I had seen the idea to back a dynamic object with a Dictionary in an MSDN article describing the DynamicObject class in .NET 4. The purpose of that was to demonstrate how you could change the behavior of the property name routing through TryGetMember/TrySetMember (in this case, to all lower-case); I'm sure this is what the IronRuby team used some of to map into ruby-style method and property names...but I digress.

Happy Birthday, HyperDynamo!

I created my own class, inherting from DynamicObject, backed by any implementation of IDictionary<string, object> that overrides the TryGetMember and TrySetMember and maps property names to key names directly and stores the value in the value portion of the KeyValuePair. I suck at naming, so Hyper ("more than", and also my Ayende-esque "Rhino-type" prefix) and Dynamo (for "dynamic") because it's more than the basic DynamicObject class. I also implemented the indexer on a string name and IEnumerable<KeyValuePair<string, object>> so that I could access properties via:

  1. Dot-notation
  2. Indexer (e.g., foo["bar"]) for both setters and getters
  3. And use foreach to iterate through the properties

It's Your Birthday, Go HyperDynamo!

So, with ease, I can now do JavaScript-style assignment, retrieval, and iteration on HyperDynamo objects in C#! Take a look:

dynamic person = new HyperDynamo();
person.FirstName = "Tony";
person.LastName = "Heupel";
person["MiddleInitial"] = "C";

Console.WriteLine("Hello, {0}, {1}., {2}!", person["FirstName"],
                              person.MiddleInitial, person.LastName);

This displays what you would expect:

Hello, Tony C. Heupel!

That's Cute, But Hardly JavaScript

Very true. Again, my original goal was not to create JavaScript in C#, but rather to enable some JavaScript (and other dynamic language) style for reducing complexity of mundane work and impedance mismatch. But, did you notice something? While HyperDynamo uses a Dictionary<string, object> by default, it really only requires any object implementing IDictionary<string, object>--including a HyperDictionary with Prototype-style inheritance!

Next Up -- Part 3: HyperHypo

The sucky names continue, but things get interesting once you start combining what I have placed into my HyperCore assembly together, mix in a pinch of closures, and top it off with a smidge of real JavaScript concepts...check out Hyper JS Episode 3 - Revenge of the Script!

No comments: