Fancy Ruby stuff done in C#, part 3: dynamic properties

Please read this post for my reasons behind this article series.

Some people have asked me, why I try to do things in C# that are clearly Ruby-ideoms. The short answer is: Because I can. The longer answer involves thoughts about that you should never stop learning. For a programmer, this involves pushing your tools to their boundaries and beyond. Seeking new insights other places and apply them to your environment. Stuff like that.

Catch-all property (like Ruby’s method_missing)

One of the powerful features of Ruby is method_missing. With this seemingly innocent construct you are able to make a class respond to things that are not statically defined beforehand.

Typical uses are for ORMs like ActiveRecord, that enables you to map class properties to database table-fields without explicitly defining the fieldnames in your class.

The way ActiveRecord does this is, that the model base-class contains a Hash (Dictionary) called attr. When a table-record is loaded, all fields are loaded into this hash. And method_missing are then used to map property-names directly to keys in the hash.

user = User.find(13)
puts user.email

This can also be done in C# using DynamicObject. http://msdn.microsoft.com/en-us/library/system.dynamic.dynamicobject.aspx

DynamicObject is a rather bold introduction into a strong-typed language like C#, since it expose members such as properties and methods at run time, instead of at compile time.

using System;
using System.Collections.Generic;
using System.Dynamic;

public class MyFakeORM : DynamicObject {
    // For a clearer example, I don't go into the stuff about loading data from the database into this model

    // Dictionary to hold all fields from the loaded record
    Dictionary _attr = new Dictionary();

    // Catch-all methods for getting and setting a "missing" property
    public override bool TrySetMember(SetMemberBinder binder, object value) {
        _attr[binder.Name] = value;
        return true;
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result) {
        return _attr.TryGetValue(binder.Name, out result);
    }
}


Note: For this to work, you also need to define your instance-variable as a dynamic instead of MyFakeORM:

dynamic user = new MyFakeORM();

user.email = "carsten@sarum.dk";

Console.WriteLine(user.email);

If you want to enable access with both properties as shown above AND as a common Dictionary, you need to add setters and getters for the class:

using System;
using System.Collections.Generic;
using System.Dynamic;

public class MyFakeORM : DynamicObject {
    ...

    public object this[string key] {
        get {
            return _attr[key];
        }
        set {
            _attr[key] = value;
        }	
    }	

    ...
    // The rest is as before
}

Now all of the following is valid:

dynamic user = new MyFakeORM();

user.email = "carsten@sarum.dk";
user['email'] = "carsten@sarum.dk";

Console.WriteLine(user.email);
Console.WriteLine(user['email']);

Skriv et svar

Din e-mail-adresse vil ikke blive offentliggjort. Krævede felter er markeret med *

Disse HTML koder og attributter er tilladte: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>