Подтвердить что ты не робот

Сравнение динамических объектов в С#

Каков наилучший способ сравнить два произвольных динамических объекта для равенства? Например, эти два объекта.

т.е.

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 из динамического типа?

4b9b3361

Ответ 1

Microsoft API для динамического вызова методов и свойств на произвольные динамические объекты (IDynamicMetaObjectProvider) не прост в использовании, если у вас нет справки компилятора. Вы можете использовать Dynamitey (через nuget), чтобы упростить это полностью. Он имеет статическую функцию Dynamic.InvokeGet, чтобы вызвать свойства getters с только целью и именем свойства.

Чтобы получить список свойств динамического объекта, есть бит gotcha, поскольку динамический объект должен его поддерживать (если это DynamicObject, который означает реализацию GetDynamicMemberNames, 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, было бы проще просто следовать типичным правилам для использования Equals, там действительно не отличается от нединамических объектов, и просто сравните то, что вы внутренне используете для состояния.

Ответ 2

ExpandoObject реализует ICollection<KeyValuePair<string, object>> (в дополнение к IDictionary и IEnumerable того же самого), поэтому вы можете легко сравнить их свойство по свойству:

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

Ответ 3

Вам нужно реализовать IComparable -Interface. Затем у вас есть необходимые функции из .NET/С# для сравнения двух объектов друг с другом.

Ответ 4

Объекты Expando можно использовать как IDictonary<string, object>, чтобы вы могли использовать их.

Что-то вроде

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

Изменить AreEqual не будет.

Но вы могли бы попробовать сравнить два словаря довольно просто.