c#中动态对象的比较

本文关键字:比较 对象 动态 | 更新日期: 2023-09-27 18:06:53

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

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 ?

c#中动态对象的比较

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

要获得动态对象的属性列表,有一点问题,因为动态对象必须支持它(如果它是一个DynamicObject,这意味着实现GetDynamicMemberNames, Expando支持它,但随机iddynamicmetaobjectprovider可能不,只是返回一个空列表)。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子类,那么遵循实现Equals的典型规则会更容易,与非动态对象没有什么区别,只是比较你内部使用的状态。

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

参见"枚举和删除成员"获取ExpandoObject的成员http://msdn.microsoft.com/en-us/library/system.dynamic.expandoobject.aspx

任意动态对象似乎不公开枚举数。

必须实现iccomparable - interface。然后你就有了。net/c#中需要的适当的函数来比较两个对象。

你也可以使用GitHub上的ObjectsComparer库:ObjectsComparer

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

查看Valerii Tereshchenko的优秀论文了解更多细节。

Expando Objects可以作为IDictonary<string, object>使用,所以你应该能够使用它。

比如

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

编辑 AreEqual不能工作

但是你可以简单地比较一下这两本字典