比较C#中的动态对象

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

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?

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

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

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

 //using System.Dynamic; //using Dynamitey; //using System.Linq; IEnumerable list1 =Dynamic.GetMemberNames(obj1); list1 = list1.OrderBy(m=>m); IEnumerable 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> (除了IDictionaryIEnumerable之外),因此您应该能够非常轻松地按属性比较它们:

 public static bool AreExpandosEquals(ExpandoObject obj1, ExpandoObject obj2) { var obj1AsColl = (ICollection>)obj1; var obj2AsDict = (IDictionary)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; } 

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

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

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

Expando对象可用作IDictonary因此您应该能够使用它。

就像是

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

编辑 AreEqual将无法正常工作。

但你可以尝试相当简单地比较两个词典。