Dynamic types for dynamic world

Some friends of mine hate JavaScript and the main reason is the dynamic nature of it. Definitely, fuzzy typing allows for quite a space for errors, yet with responsibility it also brings the freedom. Let’s accept the agility of our world. And as you interact with it, it’s great to have some dynamic tools as well.

I’m working on a small project to get and parse data form SEC.gov. Data there is well structured, but it widely varies. Different types of posted forms have different sets of fields. Adding all of them to the class is not the option. Format of data to parse is kind of XML, but not really. It has the same issue as AJAX posts to the server have. You get JSON string and then you want to convert it to some structured object with elements and lists. Newtonsoft’s JsonConvert handles it very well. Even if the structure is uncertain.

Before dynamic types in C# common practice was to use Hashtable for dynamic structures. But as for me dynamic keyword is much handier then Hashtable. Especially when it comes to debugging.

Let’s mimic JavaScript in C#

When you want to build a dynamic structure based on parsed object the great option is ExpandoObject. Unfortunately, it doesn’t handle undefined elements and when you try to access them you get exception. We can handle it with try-catch or with reflection but it’s too artificial to my mind. JavaScript has typeof(obj) === ‘undefined’ check though. So, let’s create a class that supports both ExpandoObject ability to add properties dynamically and handles undefined properties:

public class Dynamic : DynamicObject
{
    // We store all properties here
    Dictionary<string, object> properties = new Dictionary<string, object>();

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        if (properties.ContainsKey(binder.Name))
        {
            result = properties[binder.Name];
            return true;
        }
        else
        {
            // in case property does not exists we return Undefined type
            result = new Undefined();
            return true;
        }
    }

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        properties[binder.Name] = value;
        return true;
    }

    public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
    {
        dynamic method = properties[binder.Name];
        result = method(args[0].ToString(), args[1].ToString());
        return true;
    }

    // We need that to have values in debugger in object preview
    public override IEnumerable<string> GetDynamicMemberNames()
    {
        return properties.Keys;
    }

    public void AddProperty(string propertyName, object propertyValue, bool isCollection = false)
    {
        propertyName = propertyName.Replace("-", "_");

        if (isCollection) AddPropertyToCollection(propertyName, propertyValue);
        else
        {
            if (properties.ContainsKey(propertyName))
                properties[propertyName] = propertyValue;
            else
                properties.Add(propertyName, propertyValue);
        }
    }

    public void AddPropertyToCollection(string propertyName, object propertyValue)
    {
        propertyName = propertyName.Replace("-", "_");
        if (properties.ContainsKey(propertyName))
        {
            // Conver to collection first element if it's there
            if (properties[propertyName].GetType() != typeof(List<object>))
            {
                var val = properties[propertyName];
                properties[propertyName] = new List<object> { val };

            }

            ((List<object>)properties[propertyName]).Add(propertyValue);
        }
        else
            properties.Add(propertyName, new List<object> { propertyValue });
    }

    public Dictionary<string, object> GetProperties()
    {
        return properties;
    }
}

public class Undefined { } 

Usage

Now when we have the new type we can create dynamic structures with it:

dynamic dynType = new Dynamic();

// Add property with C# syntax
dynType.NewProperty = 1;

// Add property with new type meothds
(dynType as Dynamic).AddProperty("OtherProperty", "value");

Also, we can test if property exists in object:

if(dynType.NotExists is Undefined) Console.WriteLine("Property does not exists");

And even go loop over properties similar way as we do in JS:

foreach (var prop in ((Dynamic)dynType).GetProperties())
{
    Console.WriteLine("Property: {0}, value: {1}", prop.Key, prop.Value);
}

Conclusion

When you interact with the world and have a lot of uncertainty, dynamic keyword is a great tool. But, of course, you shouldn’t use dynamic everywhere. When we work with clearly defined structures, which is in most cases, we must leverage strong typing in C#.

Be the first to comment

Leave a Reply

Your email address will not be published.


*