比较C#中的动态对象

时间:2011-08-27 16:33:19

标签: c# dynamic

比较两个任意动态对象是否相同的最佳方法是什么?例如这两个对象。

dynamic obj1 = new ExpandoObject();
obj1.Name = "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); // ?

或者有没有办法将实际属性及其值作为列表?要从动态类型创建ExpandoObject,例如?

6 个答案:

答案 0 :(得分:10)

ExpandoObject实现ICollection<KeyValuePair<string, object>>(除IDictionaryIEnumerable之外),因此您应该能够非常轻松地按属性比较它们:

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

答案 1 :(得分:9)

当您没有编译器的帮助时,用于动态调用任意动态对象(IDynamicMetaObjectProvider)上的方法和属性的Microsoft API不容易使用。您可以使用Dynamitey(通过nuget)完全简化此操作。它有一个静态函数Dynamic.InvokeGet来调用属性的getter只有一个目标和一个属性名。

要获取动态对象的属性列表,有一些问题,因为动态对象必须支持它(如果它是一个表示实现GetDynamicMemberNames的DynamicObject,则Expando支持它,但随机IDynamicMetaObjectProvider可能不支持它)只返回一个空列表)。 Dynamitey还有一种简化获取这些名称的方法Dynamic.GetMemberNames

这两个函数都为您提供了通过属性比较许多任意动态对象所需的基本工具。

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

但是,如果它们只是你自己的DynamicObject子类,那么就更容易遵循典型的rules for implementing Equals,与非动态对象没有区别,只是比较你在内部使用的内容状态。

答案 2 :(得分:1)

您必须实施IComparable - 界面。然后,您将拥有.NET / C#所需的相应功能,以便将两个对象相互比较。

答案 3 :(得分:1)

Expando对象可用作IDictonary<string, object>,因此您应该可以使用它。

这样的东西
Assert.AreEqual((IDictonary(object, string))obj1, (IDictonary(object, string))obj2); 

编辑 AreEqual无效。

但你可以相当简单地比较这两本词典。

答案 4 :(得分:1)

请参阅“枚举和删除成员”以获取ExpandoObject的成员http://msdn.microsoft.com/en-us/library/system.dynamic.expandoobject.aspx

但是,任意动态对象似乎不会公开枚举器。

答案 5 :(得分:0)

您还可以使用GitHub上的ObjectsComparer库: ObjectsComparer

此库是一个对象到对象的比较器,它使我们可以逐个成员地递归比较对象,并为某些属性,字段或类型定义自定义比较规则。它支持枚举(数组,集合,列表),多维数组,枚举,标志和动态对象(ExpandoObject,DynamicObject和编译器生成的动态对象)。

转到 Valerii Tereshchenko优秀论文以获取更多详细信息。