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 > ();
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;
}
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);
}
}
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);
}
}
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:
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.
And, this is what you'll see if you check the generated XML:
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:
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.
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.
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.
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.