Unusual uses of ExpandoObject in C# 4

One of the new features in C# 4 is the use of the dynamic keyword, allowing types to be defined with no regard to static type checking.  Although many of the use cases for this revolve around interoperability, especially with dynamic languages and COM, there are types in the framework designed to make this available for use internally in C# programs as well.  The simplest dynamic implementation in .NET 4 is System.Dynamic.ExpandoObject.

Most of the examples for using ExpandoObject basically demonstrate a replacement for a Hashtable or Dictionary<string,object>, but with a “nicer” syntax.  For example, Alexandra Rusina blogged about using ExpandoObject, showing how you can use this to simplify access to XML documents.

In general, this basically makes ExpandoObject a property bag – but the XML sample could have easily been written using a dictionary, if with a bit more verbose of a syntax.

However, there are some use cases for ExpandoObject that go above and beyond what can easily be done with a dictionary.

First off, ExpandoObject implements INotifyPropertyChanged.  This means that we can have notifications as values are set on the ExpandoObject (which is not available for Dictionary<TKey,TValue> and Hashtable), but more importantly, means we can data bind cleanly to an ExpandoObject with Windows Presentation Foundation’s data binding.

Secondly, ExpandoObject can take delegates as members, which can be added and removed at runtime dynamically.  This lets us attach “methods” to our dynamic types on the fly, and have them look and feel just like a standard type.

Finally, ExpandoObject has a built-in (currently undocumented) feature that allows you to define events directly on it.  The syntax is a little odd (you have to add a member set to null first), but you can dynamically add an event, and then add subscribers, all at runtime.

Here’s a short program that demonstrates all of the unusual features of ExpandoObject, including using events, delegates, and standard members with values set:

namespace CSharpConsoleApplication
{
    using System;
    using System.ComponentModel;
    using System.Dynamic;

    class Program
    {
        // Simple event handler
        static void HandleEventOne(object sender, EventArgs e)
        {
            Console.WriteLine("HandleEventOne called");
        }
        
        // Another simple event handler
        static void HandleEventTwo(object sender, EventArgs e)
        {
            Console.WriteLine("HandleEventTwo called with: {0}", sender);
        }
        
        // Event handler for INotifyPropertyChanged
        static void dynamicObject_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            Console.WriteLine("Property {0} set or changed", e.PropertyName);
        }

        static void Main(string[] args)
        {
            // Create an ExpandoObject, and set it to a dynamic variable
            dynamic dynamicObject = new ExpandoObject();
            
            // Subscribe to INotifyPropertyChanged.PropertyChanged
            ((INotifyPropertyChanged)dynamicObject).PropertyChanged += new PropertyChangedEventHandler(dynamicObject_PropertyChanged);

            // Now set some values - our handler will run each time we set a value
            dynamicObject.MyValue = 3;

            // To use events, you must initialize to null first. Do this, then subscribe.
            dynamicObject.MyEvent = null;
            dynamicObject.MyEvent += new EventHandler(HandleEventOne);
            dynamicObject.MyEvent += new EventHandler(HandleEventTwo);

            // Add an Action and a Func<T,TResult> "method" at runtime
            dynamicObject.MyMethod = new Action(() => Console.WriteLine("Called method defined in object"));
            dynamicObject.MyFunc = new Func<int,int>((i) => i*i);

            // Raise our event!
            EventHandler e = dynamicObject.MyEvent;
            if (e != null)
            {
                e(dynamicObject, EventArgs.Empty);
            }

            // Call our "methods" and use our value
            dynamicObject.MyMethod();
            Console.WriteLine("MyFunc returns {0} for {1}", dynamicObject.MyFunc(dynamicObject.MyValue), dynamicObject.MyValue);

            // Wait for a keypress to exit
            Console.WriteLine();
            Console.WriteLine("Press any key to exit...");
            Console.ReadKey();
        }
    }
}

About Reed
Reed Copsey, Jr. - http://www.reedcopsey.com - http://twitter.com/ReedCopsey

Comments

7 Responses to “Unusual uses of ExpandoObject in C# 4”
  1. Cyril Gupta says:

    Nice information… ExpandoObjects really do expand! Going to read Alexandra’s article now.

    Thanks

  2. Andrew Hare says:

    Nice article Reed! This is a very interesting direction for C# and I am excited to learn more about it – thanks for the information!

  3. I’ve enjoyed reading your blog, both the .NET 4.0 and MVVM posts.

    What do you think about using ExpandoObject for ViewModel types?

    -Steve

    • Reed says:

      I think ExpandoObject may have some potential here, although this isn’t something I personally have tried. The nice thing about ExpandoObject is that it does implement INotifyPropertyChanged, so it may be appropriate for (simple) ViewModel scenarios.

      That being said, I typically also provide validation and other logic in the ViewModel – in which case ExpandoObject may not be enough to supply the necessary interfaces. You’ll have to evaluate your specific use case, though, to see if it makes sense.

  4. Hi Reed,

    Thanks for the article!
    And just FYI, the documentation for this feature became available with the last documentation update for VS 2010 RC.
    http://msdn.microsoft.com/en-us/library/system.dynamic.expandoobject(VS.100).aspx

Trackbacks

Check out what others are saying about this post...
  1. [...] many of the use cases for this revolve around interoperability, especially with dynamic … <http://reedcopsey.com/?p=86&gt; Reed Copsey, Jr. [...]



Speak Your Mind

Tell us what you're thinking...
and oh, if you want a pic to show with your comment, go get a gravatar!