Click here to Skip to main content
15,879,326 members
Articles / All Topics

ExpandoObject in Practice

Rate me:
Please Sign up or sign in to vote.
5.00/5 (1 vote)
13 Oct 2010CPOL4 min read 11.4K   1
ExpandoObject in practice

It sounds like a joke but there really is a class called ExpandoObject. When I first heard of it, I thought it was a farce, but it is actually pretty useful. [Let me preface this by saying I wouldn’t necessarily use this class in production code because it has the potential to be a maintenance nightmare but it’s a good place to learn about dynamic objects.] The ExpandoObject is a DyanamicObject and a DynamicObject is essentially an object that accepts and brokers member calls (methods and properties) made under the dynamic context (sort of like a proxy). ExpandoObject is a type of DynamicObject with a very specific implementation; it defines and overrides specific operations that can be performed on an object (like an actual method call or property set). It then creates properties at runtime that can be executed. The full documentation of the ExpandoObject can be found here.

So what does it mean to create properties at runtime? With ExpandoObject, you can do things like this:

C#
// Create an ExpandoObject. Declare it "dynamic".
dynamic ex = new System.Dynamic.ExpandoObject();

ex.Name = "test"; // Creates a simple property of type string.
Console.WriteLine(ex.Name);

In this code example, I’ve created an instance of ExpandoObject and assigned it to dynamic variable, example, Variables marked as dynamic are not compile-time checked, but are runtime resolved.

<font color="#111111" face="Segoe UI, Arial, sans-serif"><span style="font-size: 14px;">ex.</span></font>Name is the first property I create. I am going to make it of type string by assigning it a string value. This compiles fine even though I’ve never defined the property “Name;” I am going to let ExpandoObject create it for me.

Under most circumstances, ex.Name would fail at runtime because there is no runtime implementation. In other words, if you were to do run the following code, you would get an error:

C#
dynamic v = new Object();
v.Name = "test"; 

Console.WriteLine(v.Name);

image

An exception is thrown because “Name,” even though it compiled, does not exist at runtime.

You can also define methods on ExpandoObject.

C#
// We can also create functions (this action takes no parameters).
  ex.PrintToConsole = (Action)(() => Console.WriteLine("some function")); 

  // Creating methods that take a parameter.
  ex.PrintWithParam = (Action<string>)((string s) => Console.WriteLine(s)); 

  // Creating methods that take a parameter and return a value.
  ex.MethodWithReturn = (Func<int, int>)((int i) => i * i); 

  ex.PrintWithParam("hello");
  Console.WriteLine(ex.MethodWithReturn(9));

In the code example above, I defined the method PrintToConsole to be a delegate of type action (does not take any parameter and returns void); you can also create delegates of type Action<> and Func<>… this is a very slick way of defining dynamic methods.

So how does this all work? How does the ExpandoObject know to intercept method calls and define properties? Well, as I mentioned above, the ExpandoObject is a DynamicObject (it’s actually an IDynamicMetaObjectProvider, but DynamicObject implements IDynamicMetaObjectProvider). There are many overridable methods in DynamicObject but for our purposes, the three most important are: TryGetmember(,,.), TrySetMember(…), and TryInvokeMember(…). When implemented in a subclass, the DLR will call the appropriate method during runtime. So, for instance, when a property is set on an instance of ExpandoObject, the DLR calls TrySetMember (with some parameters) on ExpandoObject then ExpandoObject does stuff and returns either true or false; true if the call was successful (which it always is), false otherwise. If false is returned, you will get the error above (object does not contain a definition for blah blah blah). I wrote an absurdly simple implementation of my own ExpandoObject for demonstration purposes.

image

Not much to it, really. When a setter is called, TrySetMember will be executed. The binder parameter knows the name of the property that was set… so if you have ex.MyProp = “text”, the binder.Name property will be set to “MyProp.’ In my case, I use this as a key into a hashtable…the value is the second parameter. Now when a get member is executed, TryGetMember will be executed with similar parameters: the name of the property that was executed and the result, which you must set before exiting the method. The result is literally the result of the operation. So if I have a property called MyProp with a value “text,” when I go to print that to the screen, I should see “text,’ because that’s what I stored in the dictionary when the property was set.

TryInvokeMember is a little more interesting. This is called when a method is executed on a dynamic object (like in my first code example when I defined the delegates). So if I do something like ex.Something(), TryInvokeMember will be executed with all the appropriate parameters set. Since Action, Action<>, and Func<> are all delegate types, I cast the value from the dictionary to a Delegate, then dynamically invoke it with the arguments provided.

Using MySimpleExpandoObject in code.

image

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralThanks ... Pin
AWdrius13-Oct-10 22:40
AWdrius13-Oct-10 22:40 
... for a short and easy explanation. I personally have never used any of the new dynamic functionalities of the framework. It looks interesting but, as you said yourself, I doubt I would use it in production any time soon.
Trust is a weakness.

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.