开发者

Comparing dynamic objects in C#

开发者 https://www.devze.com 2023-03-30 23:57 出处:网络
What is the best way to compare two arbitrary dynamic objects for equality? For example these two objects.

What is the best way to compare two arbitrary dynamic objects for equality? For example these two objects.

I.e.

dynamic obj1 = new ExpandoObject();
obj1.Name =开发者_如何学JAVA "Marcus";
obj1.Age = 39;
obj1.LengthInMeters = 1.96;

dynamic obj2 = AMethodReturningADynamic();
obj2.Name = "Marcus";
obj2.Age = 39;
obj2.LengthInMeters = 1.96;

Assert.AreEqual(obj1, obj2); // ?

Or is there a way to get the actual properties and their values as lists? To create an ExpandoObject from a dynamic type for example?


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.


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;
}


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.


You have to implement IComparable-Interface. Then you have the appropriate functions needed from .NET/C# to compare two objects with each other.


You can also use the ObjectsComparer library available on GitHub : ObjectsComparer

This library is an object-to-object comparer that allows us to compare objects recursively member by member and to define custom comparison rules for certain properties, fields or types. It supports enumerables (arrays, collections, lists), multidimensional arrays, enumerations, flags and dynamic objects (ExpandoObject, DynamicObject and compiler generated dynamic objects).

Go to Valerii Tereshchenko excellent paper for more details.


Expando Objects are useable as an IDictonary<string, object> so you should be able to use that.

Something like

Assert.AreEqual((IDictonary(object, string))obj1, (IDictonary(object, string))obj2); 

Edit the AreEqual won't work.

But you could try comparing the two dictionaries fairly simply.

0

精彩评论

暂无评论...
验证码 换一张
取 消