Dynamic in C# 4.0: Creating Wrappers with DynamicObject

In the previous post I showed how you can use the new dynamic feature and the ExpandoObject class to add and remove properties at run time, and how this can make your code more readable and flexible than code written with LINQ to XML syntax.

But there were some obvious flaws in that example: While ExpandoObject provided better syntax, LINQ to XML provided a lot of useful library methods that helped you to work with XML files. So, is it possible to combine those two advantages, to have better syntax and still get all those methods? The answer is yes, but you need another type from the System.Dynamic namespace: DynamicObject .

The DynamicObject class enables you to override operations like getting or setting a member, calling a method, or performing any binary, unary, or type conversion operation. To illustrate the issue, let’s create a very simple object that overrides the “get property” operation, so that whenever you access a property it returns the property’s name as a string. This example has no practical value.

 public class SampleObject : DynamicObject
{
    public override bool TryGetMember(
        GetMemberBinder binder, out object result)
    {
        result = binder.Name;
        return true;
    }
}

As with ExpandoObject, we must use the dynamic keyword to create an instance of this class.

 dynamic obj = new SampleObject();
Console.WriteLine(obj.SampleProperty);
//Prints "SampleProperty".

Let’s see what’s going on in this example. When you call obj.SampleProperty, the dynamic language runtime (DLR) uses the language binder to look for a static definition of this property in the SampleObject class. If there is no such property, the DLR calls the TryGetMember method. This method gets information about what property it was called for through the binder parameter. As you can see, the binder.Name contains the actual name of the property.

The TryGetMember method returns true if the operation is successful. But the actual result of the operation must be assigned to the out parameter result. In this example, TryGetMember returns true, but obj.SampleProperty returns "SampleProperty".

Now let’s move to a more complex example and create a wrapper for the XElement object. Once again, I’ll try to provide better syntax for the following LINQ to XML sample.

 XElement contactXML =
    new XElement("Contact",
    new XElement("Name", "Patrick Hines"),
    new XElement("Phone", "206-555-0144"),
    new XElement("Address",
        new XElement("Street1", "123 Main St"),
        new XElement("City", "Mercer Island"),
        new XElement("State", "WA"),
        new XElement("Postal", "68042")
    )
);

First of all, I need to create an analog of ExpandoObject. I still want to be able to dynamically add and remove properties. But since I am essentially creating a wrapper for the XElement type, I’ll use XElement instead of the dictionary to maintain the properties.

 public class DynamicXMLNode : DynamicObject
{
    XElement node;
    public DynamicXMLNode(XElement node)
    {
        this.node = node;
    }
    public DynamicXMLNode()
    {
    }
    public DynamicXMLNode(String name)
    {
        node = new XElement(name);
    }
    public override bool TrySetMember(
        SetMemberBinder binder, object value)
    {
        XElement setNode = node.Element(binder.Name);
        if (setNode != null)
            setNode.SetValue(value);
        else
        {
            if (value.GetType() == typeof(DynamicXMLNode))
                node.Add(new XElement(binder.Name));
            else
                node.Add(new XElement(binder.Name, value));
        }
        return true;
    }
    public override bool TryGetMember(
        GetMemberBinder binder, out object result)
    {
        XElement getNode = node.Element(binder.Name);
        if (getNode != null)
        {
            result = new DynamicXMLNode(getNode);
            return true;
        }
        else
        {
            result = null;
            return false;
        }
    }
}

And here is how you can use this class.

 dynamic contact = new DynamicXMLNode("Contacts");
contact.Name = "Patrick Hines";
contact.Phone = "206-555-0144";
contact.Address = new DynamicXMLNode();
contact.Address.Street = "123 Main St";
contact.Address.City = "Mercer Island";
contact.Address.State = "WA";
contact.Address.Postal = "68402";

Let’s look at the contact object. When this object is created, it initializes its inner XElement. If you set a property value, like in contact.Phone = "206-555-0144", the TrySetMember method checks whether an element with the name Phone exists in its XElement. If it does not exist, the method creates the element.

The next interesting line is contact.Address = new DynamicXMLNode(). Basically, in this particular example this line means that I want to create a node that has subnodes. For this property, the TrySetMember method creates an XElement without a value.

The most complex case here is a line such as contact.Address.State = "WA". First, the TryGetMember method is called for contact.Address and returns a new DynamicXMLNode object, which is initialized by the XElement with the name Address. (Theoretically, I could have returned the XElement itself, but that would make the example more complicated.) Then the TrySetMember method is called. The method looks for the State element in contact.Address. If it doesn’t find one, it creates it.

So I have successfully created the required XML structure. But TryGetMember always returns an instance of DynamicXMLNode. How do I get the actual value of the XML node? For example, I want the following line to work, but now it throws an exception.

 String state = contact.Address.State;

I have several options here. I can modify the TryGetMember method to return actual values for leaf nodes, for example. But let’s explore another option: override type conversion. Just add the following method to the DynamicXMLNode class.

 public override bool TryConvert(
    ConvertBinder binder, out object result)
{
    if (binder.Type == typeof(String))
    {
        result = node.Value;
        return true;
    }
    else
    {
        result = null;
        return false;
    }
}

Now whenever I have an explicit or implicit type conversion of the DynamicXMLNode type, the TryConvert method is called. The method checks what type the object is converted to and, if this type is String, the method returns the value of the inner XElement. Otherwise, it returns false, which means that the language should determine what to do next (in most cases it means that you’re going to get a run-time exception).

The last thing I’m going to show is how to get access to the XElement methods. Let’s override the TryInvokeMember method so that it will redirect all the method calls to its XElement object. Of course, I’m using the System.Reflection namespace here.

 public override bool TryInvokeMember(
    InvokeMemberBinder binder, 
    object[] args, 
    out object result)
{
    Type xmlType = typeof(XElement);
    try
    {
        result = xmlType.InvokeMember(
                  binder.Name,
                  BindingFlags.InvokeMethod |
                  BindingFlags.Public |
                  BindingFlags.Instance,
                  null, node, args);
        return true;
    }
    catch
    {
        result = null;
        return false;
    }
}

This method enables you to call XElement methods on any node of the DynamicXMLNode object. The most obvious drawback here is absence of IntelliSense.

I’m not even going to pretend that this example is a ready-to-use wrapper for the LINQ to XML library. It doesn’t support attributes, doesn’t allow you to create a collection of nodes (for example, multiple contacts), and it is probably missing other features. Creating a library is a difficult task, and creating a good wrapper is too. But I hope that after reading this blog post you can create a fully functioning wrapper with DynamicObject yourself.

So, if you routinely use a library with complicated syntax that crawls XML files or works with script objects, or if you are creating such a library yourself, you should probably consider writing a wrapper. Doing this might make you more productive and the syntax of your library much better.

All the examples provided in this blog post work in the just released Visual Studio 2010 Beta 2. If you have any comments or suggestions, you are welcome to post them here or contact the DLR team at https://www.codeplex.com/dlr. You can also send an e-mail to the DLR team at dlr@microsoft.com.

Documentation for DynamicObject is also available on MSDN (check out our new MSDN design and don’t forget to take a look at the lightweight view.) In documentation, you can read about other useful methods of this class, such as TryBinaryOperation, TryUnaryOperation, TrySetIndex, and TryGetIndex.