Click here to Skip to main content
15,879,096 members
Articles / Programming Languages / C# 4.0

C# 4.0: DXLinq, a DynamicObject example

Rate me:
Please Sign up or sign in to vote.
3.91/5 (5 votes)
5 Jun 2009MIT1 min read 18.1K   11   2
System.Xml.Linq is a great library, it lets us very easily manipulate XML, in very close fashion to how we would interact with other data sources. My only issue is that it tends to seem a little redundant...

System.Xml.Linq is a great library, it lets us very easily manipulate XML, in very close fashion to how we would interact with other data sources. My only issue is that it tends to seem a little redundant, given this XML:

XML
<A>
  <B X="1"/>
  <B X="2"/>
</A>

We would do the following to get the value of both the X attributes:

C#
var root = XElement.Load("Sample.xml");

foreach (var b in root.Elements("B"))
    Console.WriteLine(b.Attribute("X").Value);

That’s not exactly the cleanest code ever, so what if we could do this instead:

C#
var root = XElement.Load("Sample.xml");

dynamic xml = new DXElement(root);

foreach (var b in xml.B)
    Console.WriteLine(b.X);

Using the combination of C# 4.0’s new dynamic type, and the DynamicObject class, getting this working is actually pretty easy. What we need is a dynamic that will map properties to child elements and attributes. The DynamicObject base class provides an overloadable TryGetMember method that is called whenever the code tries to get a property off of a dynamic. For example, when xml.B is used above, DynamicObject.TryGetMember is called, asking the DynamicObject to return the value of the member. So the DXElement gets to decide what type and value to return, at runtime. Here is my DXElement class, in its entirety:

C#
public class DXElement : DynamicObject
{
    public XElement BaseElement { get; set; }

    public DXElement(XElement baseElement)
    {
        this.BaseElement = baseElement;
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        //Go ahead and try to fetch all of the elements matching the member name, 
        //and wrap them
        var elements = BaseElement.Elements(binder.Name).Select
			(element => new DXElement(element));

        //Get the count now, so we don't have to call it twice
        int count = elements.Count();
        if (count > 0)
        {
            if (count > 1)
                result = elements; 	//We have more than one matching element, 
				//so let's return the collection
            else
                result = elements.FirstOrDefault(); //There is only one matching element,
					      //so let's just return it

            return true; //return true because we matched
        }
        else
        {
            //Ok, so no elements matched, so lets try attributes
            var attributes = 
		BaseElement.Attributes(binder.Name).Select(attr => attr.Value);
            count = attributes.Count();

            if (count > 0)
            {
                if (count > 1)
                    result = attributes; 	//more than one attribute matched, 
					//let's return the collection
                else
                    result = attributes.FirstOrDefault(); //only one attribute matched, 
							//let's just return it

                return true; // return true because we matched
            }
        }

        //no matches, let's allow the base class to handle this
        return base.TryGetMember(binder, out result);
    }
}

Not too bad right? Now, it makes a couple of assumptions about the input XML, which are most likely false, but for examples sake, we'll just ignore that fact ;). As you can see, there isn't much code needed, just the TryGetMemeber overload using XLinq against the BaseElement to find what we want to return as the result.

License

This article, along with any associated source code and files, is licensed under The MIT License


Written By
Software Developer
United States United States
I currently work as a Software Engineer for a company in North Carolina, mainly working with C#.

Comments and Discussions

 
GeneralExcel Sheet Pin
adatapost5-Jun-09 23:43
adatapost5-Jun-09 23:43 
GeneralRe: Excel Sheet Pin
StormySpike7-Jun-09 16:57
StormySpike7-Jun-09 16:57 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.