Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Adventures with C# 4.0 dynamic - ExpandoObject, ElasticObject, and a Twitter client in 10 minutes

0.00/5 (No votes)
29 Mar 2010 4  
Explores the dynamic features in C# 4.0, and a few cool things you can do with the same.

Introduction

A lot is said and done about the new (well, not so new anymore) dynamic features in .NET 4.0. At least, a few people think dynamic is pure evil. May be, may be not.

Please leave aside those arguments for a moment. The objective of this article is 'just' to explore a few cool things you can do with the dynamic features. Whether to use them or not - that is up to your discretion. Period. The related source code is rolled out in VS2010 beta, it should work with the RC as well.

Not Totally Evil - There are Useful Scenarios

There are a lot of scenarios where the dynamic features can really simplify things for you. For example, let us assume a Reflection based scenario where you load a type (from an external assembly or so) to invoke a member. Here is a quick example. You'll see how to use dynamic as an easier alternative for Reflection. This is much more evident when you deal with scenarios like COM interop etc. We'll see more interesting uses later.

Under the hood - Dynamic Language Runtime will take care of the method dispatch during run time. If you want to understand in detail how 'dynamic' is getting compiled and what it looks like in IL, read this article and have a look at the related source code there.

Things getting more interesting - DynamicObject and ExpandoObject

In dynamic languages like Ruby, there is an interesting feature called method_missing. Method missing is where your method request will end if the method you called cannot be found.

In .NET 4.0, now you can bring in the same feature, by inheriting your object from the DynamicObject class. What this means is, you'll get notified when a method/property is invoked/accessed on your object. We'll see this in detail later.

Also, .NET framework 4.0 has a cool new ExpandoObject class, in the System.Dynamic namespace. What makes this special is, it allows you to create an object with members that can be dynamically added and removed at run time.

dynamic obj = new ExpandoObject();
obj.Value = 10;
var action = new Action<string>((line) => Console.WriteLine(line));
obj.WriteNow = action;
obj.WriteNow(obj.Value.ToString());

If you run the above code, you can see 10 in the console as output (for sure:)).

Now, let us see how to create our own minimal implementation of ExpandoObject. We'll call it MyExpando. Yes, we'll inherit our MyExpando class from the DynamicObject class. The DynamicObject class has a few cool methods for you to override. For now, we are only interested in these members:

  • TrySetMember - Provides the implementation of setting a member.
  • TryGetMember - Provides the implementation of getting a member.
  • TryInvokeMember - Provides the implementation of calling a member.

So, here is the implementation of MyExpando, a minimal implementation of .NET 4.0's ExpandoObject class to reveal how that 'thingy' really works.

public class MyExpando : DynamicObject
{

    private Dictionary < string, object > _members = 
            new Dictionary < string, object > ();

    /// <summary>
    /// When a new property is set, 
    /// add the property name and value to the dictionary
    /// </summary>     
    public override bool TrySetMember
         (SetMemberBinder binder, object value)
    {
        if (!_members.ContainsKey(binder.Name))
            _members.Add(binder.Name, value);
        else
            _members[binder.Name] = value;

        return true;
    }

    /// <summary>
    /// When user accesses something, return the value if we have it
    /// </summary>      
    public override bool TryGetMember
           (GetMemberBinder binder, out object result)
    {
        if (_members.ContainsKey(binder.Name))
        {
            result = _members[binder.Name];
            return true;
        }
        else
        {
            return base.TryGetMember(binder, out result);
        }
    }

    /// <summary>
    /// If a property value is a delegate, invoke it
    /// </summary>     
    public override bool TryInvokeMember
       (InvokeMemberBinder binder, object[] args, out object result)
    {
        if (_members.ContainsKey(binder.Name) 
                  && _members[binder.Name] is Delegate)
        {
            result = (_members[binder.Name] as Delegate).DynamicInvoke(args);
            return true;
        }
        else
        {
            return base.TryInvokeMember(binder, args, out result);
        }
    }


    /// <summary>
    /// Return all dynamic member names
    /// </summary>
    /// <returns>
    public override IEnumerable<string> GetDynamicMemberNames()
    {
        return _members.Keys;
    }
}

Whenever a property set operation happens, the runtime will invoke TrySetMember, and there, we are pushing the property name and value to a dictionary. Similarly, whenever a get operation happens (TryGetMember), we are simply returning the value object if available in the dictionary.

Also, when a method call is made (TryInvokeMember), if the value type is a delegate, we just invoke the same by passing the arguments we received. Pretty simple, huh? Well, that is it. Now, let us try the above ExpandoObject scenario with our MyExpando object.

dynamic obj = new MyExpando();
obj.Value = 10;
var action = new Action<string>((line) => Console.WriteLine(line));
obj.WriteNow = action;
obj.WriteNow(obj.Value.ToString());

Things Getting *Very* Interesting - ElasticObject

Following the concepts, recently, I implemented an 'ElasticObject' class using dynamic features. First of all, let us see what exactly the ElasticObject implementation is capable of. After that, we might touch the implementation details. To start with, here are a few scenarios you can use ElasticObject in:

  • An easier, fluid way to work with data formats - like XML and JSON. Presently, we've some support for XML.
  • Cleaner code, though it is duck typed.
  • A hierarchical way to maintain loosely typed data.

What does that mean? Here we go.

1 - You can create multi level dynamic objects automatically, like this:

ElasticObject supports creating hierarchical dynamic data structures. For example, consider this code:

image

2 - Then, you can simply convert that to XML using the '>' Conversion operator

In the ElasticObject implementation, several operators have special meaning. For example, the '>' operator can be used to convert an ElasticObject instance to another data format. Now, this is what you need to do to generate an XElement representation from the above 'store' object.

image

And, this is what you'll see if you check the generated XML:

image

3 - You can convert back and forth between XML and ElasticObject

In the above example, we've seen how to convert from a 'dynamic' ElasticObject to XML. In fact, you can convert XML directly to a 'dynamic' ElasticObject, using the XElement.ToElastic() extension method in the DynamicExtensions class. See this unit test for getting a better idea:

image

If you want to read more about the ElasticObject, I've a more detailed, introductory post here in my blog. And, you can get the entire source code downloaded from the download link above - also, I'm not discussing the actual implementation of ElasticObject now in this post - you can look at the code and the related unit tests.

A 10 Minute Twitter Search App

Now, let us see what ElasticObject is capable of. Here is a quick 10 minute Twitter search client in ASP.NET MVC. Alright, to start with, have a look at the application screenshot below. It is a simple Twitter search app in ASP.NET MVC that can show you the latest tweets about what you search for.

image

The Controller

Have a look at my controller. I just created an ASP.NET MVC 2 application in VS2010, and added references to Microsoft.CSharp.dll and the AmazedSaint.Elastic project (included in the download above). The AmazedSaint.Elastic namespace contains my ElasticObject implementation I talked about previously. Basically, ElasticObject can be used as a fluent dynamic wrapper to work with data formats like XML, and it is expected to evolve as a smarter cousin of the C# 4.0 ExpandoObject.

So, let us get back to the controller. VS already created the templates for me, and I modified the Index action in HomeController like this.

image

You might have noticed that I added a 'query' parameter to the Index action. Also, I've left out the event handler, so if your Twitter connection is broken, the application will crash in your face :). We are parsing the ATOM format (it is XML) to an XElement and then creating an ElasticObject out of that. If you are *still* wondering what the heck is the ToElastic() method, you should see this. Otherwise, if you can imagine, read on.

The View

The view is also pretty minimal. Two interesting points here. Firstly, we are inheriting the page from ViewPage<dynamic>, which means the view data we returned from the controller gets passed here as a dynamic object. And everything else is pretty simple, isn't it? We are just iterating through all the elements with the name 'entry' in the Model (see the result of the Twitter API call: http://search.twitter.com/search.atom?lang=en&q=aspnetmvc), to print the image, author name, tweet text, etc.

image

According to ElasticObject conventions, '_All' after an element name will return all elements with that name as shown above. Also, adding a tilde '~' character before an element will return the content of that element. You don't need that if the accessed item is an attribute (see how we are accessing the href). Again, compare the elements we used in our view with the Twitter API returned ATOM data if you still need clarity. It is just a demo app to showcase ElasticObject - I'm not advocating duck typing :).

Oh yes, you can get the demo downloaded from the download link above.

History

  • Tuesday, March 29 - First version.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here