Whilst your average C# programmer probably considers himself superior to those poor people who habitually use VB.Net, he is at the same time silently envious of one or two (but certainly no more than that!) features of the deluded ones' language. Concise, hassle-free, and above all, Type.Missingless COM Interop for one. And deep XML integration for two. But with C#4.0, and Anders' masterly introduction of objects that can be statically typed as dynamic, that will change. Gone will be all envy; in its place, pure pity. ;-) Here's one example for why.
I finally found time (we've just finished our iteration), and an excuse (I've got to give a briefing on PDC), to delve into my smart black USB hard disk ("the goods") that I brought back from the PDC, and to fire up the Virtual Hard Drive where lives Visual Studio 2010 and C# 4.0. As an aside, I'm running it under VirtualBox, rather than Virtual PC, without any difficulties at all, and with very good performance.
In Ander's presentation about the Future of C# he showed a simple example of how the dynamic features of C# 4.0 could be used to implement a property bag, where you could write (and read) arbitrary properties on the bag and it would store whatever you assigned to it. The punch line was that he implemented it in a couple of lines of code.
That gave me the idea that it shouldn't be too difficult to simulate something like VB.Net XML integration, where you can take an XML object, and access its attributes and elements as if they were properties of the object. And it wasn't. Now don't get too excited: I spent about fifteen minutes on this, but the outcome should be enough to whet your appetite.
First, the end-result:
static void Main(string[] args) { var xml = "<Pet Type='Cat' Name='Leo'><Owner Name='Sam' Age='27'/></Pet>"; dynamic dynamicXml = new DynamicXElement(xml); Console.WriteLine("Name={0}, Type={1}", dynamicXml.Name, dynamicXml.Type); Console.WriteLine("Owner={0}, Age={1}", dynamicXml.Owner.Name, dynamicXml.Owner.Age); Console.ReadLine(); }
In line 4 I'm creating one of these new-fangled dynamic object thingies, declaring it with the magical dynamic keyword. The dynamic keyword tells the compiler to do all member resolution on the object at run time, rather than compile time. And how does it do the member resolution? That's where the DynamicXElement comes in. The DynamicXElement is equipped with the right knobs and levers so that it can participate in dynamic resolution. It must be tremendously complicated then? You'll see in a minute.
Lines 6 and 7 show off the amazing capabilities of this DynamicXElement. Having supplied it with some XML when we initialised it, we can now access values of attributes just as if they were properties of the element. Likewise with child elements and their properties. Isn't that exciting? Doesn't it deserve some applause (as the more desperate of the PDC presenters might say!)?
So how does dynamic binding work? I won't pretend to you that I understand it fully at the moment. What I do know is that if you want to have a say in how member resolution works, your objects have to implement IDynamicObject. I also know that in .Net 4.0 there will be a base implementation of that interface called DynamicObject (follow the link to get the code for it); if you inherit from that it takes care of the minor complications for you, leaving your code to be as simple as:
class DynamicXElement : System.Dynamic.DynamicObject { XElement _xml; public DynamicXElement(string xml) { _xml = XElement.Parse(xml); } public DynamicXElement(XElement element) { _xml = element; } public override object GetMember(System.Scripting.Actions.GetMemberAction info) { var attribute = _xml.Attribute(info.Name); if (attribute != null) { return attribute.Value; } return new DynamicXElement(_xml.Element(info.Name)); } }
As you can see, I only need to override one method, GetMember, to give me the control I need of dynamic behaviour (there are other overrides available if you want to handle calls to property setters or methods). This method is called whenever the runtime wants to resolve a call on a property getter. The GetMemberAction object that is passed to the method contains a Name property giving, unsurprisingly, the name of the property that's being asked for. In my ultra-simple implementation, I'm using that to first check for the existence of an attribute with that name; or, failing that, assuming that it must be the name of a child element. I'm returning the child element in a DynamicXElement wrapper so its attributes and children can be accessed in the same dynamic way.
Simple wasn't it?
But please look gently at this code. I know that it will fall to pieces in anything approaching a real-world situation (like when an element has an attribute and a child element with the same name). If you want to see some more substantial examples, look at Nikhil Kothari's blog. Here's his dynamic JSON wrapper, for instance.
3 comments:
Give the crowd what they(I) want!
Project euler!
Saevar,
I hear you! I've got something in mind but,as you can tell, I'm finding it difficult to get time for posts not inspired by the work I'm actually doing at the moment.
I know. Excuses, excuses. I'll try to do better!
No problem - I just enjoy your explained solutions of the project euler challenges(Actually how I found your site). I have used some of your explanations as inspiration to solve a couple of problems(Pyramid sums), although I wrote my own code.
Keep it up!
Post a Comment