Wednesday 18 January 2012

Cool uses of C# dynamic

There's no doubt the dynamic feature of C# is an advance one that should appeal to anyone interested in Programming languages. That said, one can also end up thinking it's one more of those things that is fun to brag about "hey, look how cool my language is", but that is not much useful in real life scenarios. I'll try to beat this thought by showing here some really interesting samples of useful application of the dynamic thing

  • Parse JSON. OK, we've got the JavaScriptSerializer and the DataContractJsonSerializer, but in both cases we need to deserialize to an existing class. What happens if we don't know upfront the properties we'll be dealing with, or we just don't want to spend time typing the corresponding class declaration? Well, we can just parse the JSON string into an ExpandoObject, which fields would be Arrays, strings, numbers or other ExpandoObjects when dealing with other objects. It's really rather simple to implement (and of course it was not an idea of mine, I just got it from here:
    public static ExpandoObject Expando(this IDictionary dictionary)
    {
        var expando = new ExpandoObject();
        var expandoDic = (IDictionary)expando;
     
        foreach (var item in dictionary)
        {
            bool alreadyProcessed = false;
     
            if (item.Value is IDictionary<string, object>)
            {
                expandoDic.Add(item.Key, Expando((IDictionary)item.Value));
                alreadyProcessed = true;
            }
            else if (item.Value is ICollection)
            {
                var itemList = new List<object>();
                foreach (var item2 in (ICollection)item.Value)
                    if (item2 is IDictionary)
                        itemList.Add(Expando((IDictionary<string, object>)item2));
                    else
                        itemList.Add(Expando(new Dictionary<string, object> { { "Unknown", item2 } }));
     
                if (itemList.Count > 0)
                {
                    expandoDic.Add(item.Key, itemList);
                    alreadyProcessed = true;
                }
            }
     
            if (!alreadyProcessed)
                expandoDic.Add(item);
        }
     
        return expando;
    }
    
  • Googling a bit we find a more powerful solution, the ElasticObject depicted in the AmazedSaint's blog. The most amazing thing of this object, and that sets it ahead of for example the inherently expandable objects that we have for example in JavaScript, or of an good-old dictionary, is that if we access a non existing property of another non existing property, both properties will be created!!! I'll give an example:
    • JavaScript:
      var ob = {};
      ob.name = "xose";
      ob.address = {};
      ob.address.street = "Leipziger strasse";
      
      //we have an exception here, the favorites property does not exist, and as we're doing a get, not a set, it crashes
      ob.favorites.book = "1984";
      
    • c#
      dynamic ob = new ElasticObject();
      ob.name = "xose";
      
      //we don't need the line below, the ElasticObject would take care of creating the missing address property as a new Elastic object if needed
      ob.address = new ElasticObject();
      ob.address.street = "Leipziger strasse";
      
      //works fine
      ob.favorites.book = "1984";
      
  • ExposedObjectProbably this is one of the most useful applications of dynamic that I've come across with, it's obvious, but finding this post was a real eye opener. In some occasions I've needed to use Reflection to get access to some private property or method. This is fine (I'm not going to discuss here whether accessing private stuff is a good idea or not), but we get a quite a lot of "noise" (the reflection operations distract us from the real aim, just getting or setting a value), and is far from wrist friendly. Well, there's a much more elegant solution, use dynamic and wrap your object in an ExposedObject. It will take care of using Reflection on its own. Notice that if we were not wrapping our object in ElasticObject and just using dynamic, the Reflection mechanism to which the C# Runtime Binder resorts would not allow access to private items.
      dynamic p1 = new Person("Iyan", 36);
      
      Console.WriteLine("using Reflection:");
      PropertyInfo pr = p1.GetType().GetProperty("Name");
      Console.WriteLine(pr.GetValue(p1, null));
      
      Console.WriteLine("using dynamic:");
      Console.WriteLine(p1.Name);
      try
      {
       Console.WriteLine(p1.age);
      }
      catch(Exception ex)
      {
       //with the normal dynamic behaviour we get an exception when trying to get access to private items
       Console.WriteLine("Exception: " + ex.ToString());
      }
      
      Console.WriteLine("using ExposedObject:");
      dynamic expP1 = ExposedObject.From(p1);
      Console.WriteLine(expP1.Name);
      Console.WriteLine(expP1.age);
    

    All this reminds me a bit of this previous post. Even if we don't use an implementation of DynamicObject, just the dynamic keyword, it's terribly useful yet (let's think of another scenario where it can be applied, we want to "deserialize" some key-value data into different objects and don't want to create specific mappers, just match keys in the data to properties in the objects). Not only we make our code much more readable, but we even get some performance gains thanks to the caching mechanism used by the DLR (as Eric Lippert confirms here)

    With Reflection you do not get any cached behaviour, which means that operations are generally slower, but there is no memory cost for maintaining the cache and every operation is roughly the same cost. With the DLR, the first operation is very slow indeed as it does a huge amount of analysis, but the analysis is cached and reused. That consumes memory, in exchange for increased speed in subsequent calls in some scenarios. What the right balance of speed and memory usage is for your application, I don't know.

    As a side note, I must say that this is a jewel of a blog, with some absolutely amazing posts as this one about GPGPU

No comments:

Post a Comment