Comparing dynamic objects in C#

ExpandoObject implements ICollection<KeyValuePair<string, object>> (in addition to IDictionary and IEnumerable of the same), so you should be able to compare them property by property pretty easily:

public static bool AreExpandosEquals(ExpandoObject obj1, ExpandoObject obj2)
{
    var obj1AsColl = (ICollection<KeyValuePair<string,object>>)obj1;
    var obj2AsDict = (IDictionary<string,object>)obj2;

    // Make sure they have the same number of properties
    if (obj1AsColl.Count != obj2AsDict.Count)
        return false;

    foreach (var pair in obj1AsColl)
    {
        // Try to get the same-named property from obj2
        object o;
        if (!obj2AsDict.TryGetValue(pair.Key, out o))
            return false;

        // Property names match, what about the values they store?
        if (!object.Equals(o, pair.Value))
            return false;
    }

    // Everything matches
    return true;
}

The Microsoft API's for dynamically invoking methods and propertys on arbitrary dynamic objects (IDynamicMetaObjectProvider) are not easy to use when you don't have the compiler's help. You can use Dynamitey (via nuget) to simplify this completely. It has a static function Dynamic.InvokeGet to call property's getters with just a target and a property name.

To get a list of properties of the dynamic object, there is a bit of a gotcha, as the dynamic object has to support it (if it's a DynamicObject that means implementing GetDynamicMemberNames, Expando supports it, but random IDynamicMetaObjectProvider may not and just return an empty list). Dynamitey has a method to simplifying getting those names as well, Dynamic.GetMemberNames.

Both of those two functions give you the basic tools necessary to compare many arbitrary dynamic objects via properties.

//using System.Dynamic;
//using Dynamitey;
//using System.Linq;

IEnumerable<string> list1 =Dynamic.GetMemberNames(obj1);
list1 = list1.OrderBy(m=>m);
IEnumerable<string> list2 =Dynamic.GetMemberNames(obj2);
list2 = list2.OrderBy(m=>m);

if(!list1.SequenceEqual(list2))
 return false;

foreach(var memberName in list1){
 if(!Dynamic.InvokeGet(obj1, memberName).Equals(Dynamic.InvokeGet(obj2,memberName))){
    return false;
 }
}
return true;

However, if they are just your own DynamicObject subclass then it'd be easier to just follow the typical rules for implementing Equals, there really is no difference from non-dynamic objects, and just compare what you are internally using for state.


See "Enumerating and deleting members" to get the members of an ExpandoObject http://msdn.microsoft.com/en-us/library/system.dynamic.expandoobject.aspx

Arbitrary dynamic objects do not appear to expose enumerators, though.

Tags:

C#

Dynamic