Support for Dynamically Typed Objects
One of the announcements that emerged from the PDC is C# 4.0’s support for dynamically typed objects. These are objects whose type is not determined until runtime. Variables that point to such types are declared with a new contextual keyword,
objects (something supported by VB but not C# until now). dynamic
`. Support was added so that API calls into dynamically type languages (such as scripting languages) could be supported. Another example where dynamically typed object support is useful is calling into
` IDispatch
In order to investigate how dynamic objects worked I decided to create a working sample that dynamically went against an XML element. Working with Michael Stokesbary, I was able to put together the following sample:
Consider the following XElement:
XElement element = XElement.Parse(
@"<Person>
<FirstName>Inigo</FirstName>
<LastName>Montoya</LastName>
</Person>"
);
From here we can assign it to a dynamic variable and read out the data as follows:
dynamic personXml = new DynamicXml(element);
Console.WriteLine("Hello, my name is {0} {1}", personXml.FirstName, personXml.LastName);
and
personXml.FirstName = "Bob";
As this code shows, with a dynamic type over XML, you can use XML element names as the property names to retrieve data from the XML element.
To implement the DynamicXML type, all you need to do is define an type that implements
members. Rudimentary implementations are shown below: System.Scripting.Actions.IDynamicObject
` with its one
` MetaObject GetMetaObject(Expression parameter)
` method. The easier way to do this is to derive from
` Dynamic
`, a class that is currently available in the Iron Python implementation. (In the future, we should expect that Microsoft will provide such a class in the framework but no specific plans on this have been announced.) Once deriving from
` Dynamic
`, the only remaining task is to override the
` object GetMember(GetMemberAction action)
` and
` void SetMember(SetMemberAction action, object value)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Scripting.Actions;
using System.Xml.Linq;
using System.Xml.XPath;
// comment here
public class DynamicXml : Dynamic
{
private XElement Element { get; set; }
public DynamicXml(System.Xml.Linq.XElement element)
{
Element = element;
}
public override object GetMember(GetMemberAction action)
{
object result = null;
XElement firstDescendant = Element.Descendants(action.Name).FirstOrDefault();
if (firstDescendant != null)
{
if (firstDescendant.Descendants().Count() > 0)
{
result = new DynamicXml(firstDescendant);
}
else
{
result = firstDescendant.Value;
}
}
return result;
}
public override void SetMember(SetMemberAction action, object value)
{
XElement firstDescendant = Element.Descendants(action.Name).FirstOrDefault();
if (firstDescendant != null)
{
if(value.GetType() == typeof(XElement))
{
firstDescendant.ReplaceWith(value);
}
else
{
firstDescendant.Value = value.ToString();
}
}
else
{
throw new ArgumentException(string.Format("Element name, '{0}', does not exist.", action.Name));
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Scripting.Actions;
using System.Xml.Linq;
using System.Xml.XPath;
public class DynamicXml : Dynamic
{
private XElement Element { get; set; }
public DynamicXml(System.Xml.Linq.XElement element)
{
Element = element;
}
public override object GetMember(GetMemberAction action)
{
object result = null;
XElement firstDescendant = Element.Descendants(action.Name).FirstOrDefault();
if (firstDescendant != null)
{
if (firstDescendant.Descendants().Count() > 0)
{
result = new DynamicXml(firstDescendant);
}
else
{
result = firstDescendant.Value;
}
}
return result;
}
public override void SetMember(SetMemberAction action, object value)
{
XElement firstDescendant = Element.Descendants(action.Name).FirstOrDefault();
if (firstDescendant != null)
{
if(value.GetType() == typeof(XElement))
{
firstDescendant.ReplaceWith(value);
}
else
{
firstDescendant.Value = value.ToString();
}
}
else
{
throw new ArgumentException(string.Format("Element name, '{0}', does not exist.", action.Name));
}
}
}
That’s it… that’s all that is needed to understand how to implement a dynamic object.
Caveats:
- Multiple elements with the same name are not supported in this implementation. To do so we wanted to use the index operator with the dynamic type but this is not supported in the PDC 2008 CTP bits – it will be.
- This example does not work with attributes. What would the syntax be if it did? One cool idea of Mike’s was to use the verbatim identifier as in personXml.@FirstName. – the XPath like way to retrieve attributes. Unfortunately, this wouldn’t work work since the @ sign is removed at compile time so the IDynamicObject.GetMember() call receives FirstName for the action Name.
- There is no support for reading the root element name. The example assumes you only navigate into the children (to avoid infinite recursion) when navigating.
In the process of writing this dynamic XML implementation, I found a few idiosyncrasies in the current dynamic type support found in the PDC 2008 CTP:
- There is no type checking for dynamic arguments. Consider the sample code below:
string text1 = "Text";
dynamic text2 = text1;
Assert.AreEqual<string>(text1, text2);
Not only does the code sample compile (by design), but it throws an exception of type
(not by design – Mads agree’s this probably isn’t correct). ArgumentNullException
You keep using that word… I do not think it means what you think it means…
Nice feature, which exist in today’s Adobe ActionScript 3.0 for Adobe Flex and Air development. Been coding like this for years now. LOL
You ask, "This example does not work with attributes. What would the syntax be if it did?", which I find odd. In your current implementation, you are building properties based on an element’s sub elements, which seems like a mistake to me. In your sample, you have the FirstName as a child element, where it should clearly be an attribute. Does a person have multiple first names? Obviously not. FirstName is an attribute of a person, same for lastname, age, ssn, etc. Now if you want to also list that person’s children, that’s where you’d want to use child elements, as they are a 1 to many relationship. With that in mind, shouldn’t you be exposing attributes as properties and child elements as generic lists of things?
Am I the only one who thinks it is overly complex ? Having a built-in support for dynamic languages in .NET is good, but adding a dynamic keyword in C# is a very very bad idea IMHO. C# is not a dynamic language, and it will not gain anything to try to be one of them. You can’t be both dynamic AND static, you have to choose.
All this begins to sound to me like adding the maximum number of (cool) features, regardless of how they fit in the existing language stack.
I dare say C# 4.0’s support for dynamically typed objects is leading to VC#A.
Cheers!
Amitabh
Hi,
um can you point the reader to how to get access to this Dynamic base type? I dont see any different namespaces than what would normally show up. You also dont include a link to where this was downloaded from. I’m totally confused here. Is this an internal thing or is there a way for anyone to do what you have done right now?
For dynamic XML attributes, could you use the index operator with string parameters? Perhaps if you want to support both multiple elements with the same name and attributes, you could have the named element be an object that also supports an indexer to access the attributes. So, if in the example above the FirstName element had an attribute named "Nickname" you could:
bool isNickName = Convert.ToBoolean(personXml.FirstName[0]["Nickname"]);
Anyhow, this is some very cool stuff. I am really looking forward to the new features in C# 4.0! I used to be a die-hard C++ guy, but C# is really winning me over.
cool it looks nice, can you give me example where this wouldl be used?