In .NET 4.0, we have access to the Dynamic Language Runtime (DLR) giving us dynamic dispatch via the IDynamicMetaObjectProvider interface coupled with the dynamic keyword. When a variable is declared as
dynamic and it implements the
IDynamicMetaObjectProvider interface, we are given the opportunity to control the delegation of calls on that object by returning a
DynamicMetaObject containing an expression which will be evaluated by the runtime. We only get this opportunity if the direct target was unable to directly handle the expression.
ExpandoObject classes both implement
IDynamicMetaObjectProvider, but their implementations are explicitly implemented and the nested classes used to return the
DynamicMetaObject are private and sealed. I understand that the classes may not have been tested enough to make them inheritable, but not having access to these classes really hurts our ability to easily modify the behavior of the underlying
DynamicMetaObject. The key word here is easily; implementing the needed expression building is a great deal of work and the internal workings of the Microsoft implementations leverage many internal framework calls.
DynamicMetaObject implementation. This will allow us to attempt to interpret the expression on the object itself or pass it along.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
This small amount of code just sets the hook. Now we need set up the delegation expression.
To set up the prototypal hierarchy, we are going to need to do a lot of recursion. Unfortunately, it is well hidden (I’ll explain shortly). When a call is made on the root object, we are given the expression being interpreted. Using the
IDynamicMetaObjectProvider overload, we will hand off the expression to the
PrototypalMetaObject to construct delegation expression (or the
DynamicObject implementation if our prototype is
null thus trying to interpret the expression on the current object). We want to make a preorder traversal of the prototype hierarchy; at each step, the current object’s evaluation will take precedence over its prototype tree. Consider the following prototypal class hierarchy:
Since we never know which level of the hierarchy will be handling the expression, we need to build an expression for the entire tree every time. We want to get the
DynamicMetaObject representing the current object’s tree first. Once done, we get the
DynamicMetaObject for evaluating the expression on the current instance. With these two, we can create a new
DynamicMetaObject which try to bind the expression to the current instance first, and then fallback to the prototype. At the root level, the prototype
DynamicMetaObject contains the same fallback for the next two layers.
There is another caveat that we need to address. When we try to invoke and expression on an object, the expression is bound to that type. When accessing the prototype, if we don’t do anything, the system will throw a binding exception because the matching object won’t match the
DynamicMetaObject’s type restrictions. To fix this, we need to relax the type restrictions for each prototype.
To iterate is human, to recurse is divine, to inception recurse is demented
Remember the recursion I mentions earlier? In the code sample below, I have pulled out all binding code except for the
BindInvokeMember method. The
_metaObject.Bind[...] will actually call into
DelegatingPrototype::GetMetaObject which will try to call back into
_metaObject.Bind[...], which will…well you get the idea. At each call, the prototype becomes the target and we get a new prototype.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
You may be thinking, ok, this is cool, but what use it is it? What is the use case? First, it’s cool. Second, it sets the foundation for .NET mixins. Third, it gives us a second form of inheritance (after parasitic) for PowerShell.
What if we take the prototype and make it a collection of prototypes? What if instead of inheriting from
DelegatingPrototype we reuse the internal prototypal skeleton? If this sounds familiar, it should. I am describing ruby classes with modules and a base class, but with C#…
If you want to see more or play around with the code, you can find full implementations in the Archetype project.