Comparación de objetos dinámicos en C #

¿Cuál es la mejor manera de comparar la igualdad de dos objetos dinámicos arbitrarios? Por ejemplo, estos dos objetos.

Es decir

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

¿O hay alguna forma de obtener las propiedades reales y sus valores como listas? ¿Para crear un ExpandoObject a partir de un tipo dinámico, por ejemplo?

preguntado el 27 de agosto de 11 a las 16:08

Hmmm, creo que he visto esto mal. Podría tener que crear una clase para mi objeto y heredar de DynamicObject (msdn.microsoft.com/en-us/library/…). Entonces puedo usar métodos como GetDynamicMemberNames (msdn.microsoft.com/en-us/library/…) y TryGetMember (msdn.microsoft.com/en-us/library/…) para consultar y comparar los objetos. -

6 Respuestas

ExpandoObject implementos ICollection<KeyValuePair<string, object>> (además de IDictionary y IEnumerable del mismo), por lo que debería poder compararlos propiedad por propiedad con bastante facilidad:

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

Respondido 27 ago 11, 20:08

Gran respuesta, pero mi pregunta era engañosa. Estaba tratando de comparar con objetos dinámicos. El hecho de que fueran ExpandoObject fue solo mi ejemplo de creación del objeto dinámico. Los objetos dinámicos arbitrarios no parecen exponer a los enumeradores, como respondió Raymond. - Marcus Hammarberg

¿Cómo ampliaría este ejemplo para admitir la recursividad? Los ExpandoObjects suelen tener elementos secundarios de tipo ExpandoObject. ¿Podemos usar esto para ExpandoObjects de varios niveles? Gracias. - Alexandru Dicu

Las API de Microsoft para invocar dinámicamente métodos y propiedades en objetos dinámicos arbitrarios (IDynamicMetaObjectProvider) no son fáciles de usar cuando no se cuenta con la ayuda del compilador. Puedes usar Dinamita (a través de nuget) para simplificar esto por completo. Tiene una función estática Dynamic.InvokeGet para llamar a los captadores de propiedades con solo un objetivo y un nombre de propiedad.

Para obtener una lista de propiedades del objeto dinámico, hay un poco de error, ya que el objeto dinámico tiene que admitirlo (si es un DynamicObject que significa implementar GetDynamicMemberNames, Expando lo admite, pero el IDynamicMetaObjectProvider aleatorio puede no y solo devolver un lista vacía). Dynamitey también tiene un método para simplificar la obtención de esos nombres, Dynamic.GetMemberNames.

Ambas funciones le brindan las herramientas básicas necesarias para comparar muchos objetos dinámicos arbitrarios a través de propiedades.

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

Sin embargo, si son solo su propia subclase de DynamicObject, entonces sería más fácil seguir el típico reglas para implementar Equals, realmente no hay diferencia con los objetos no dinámicos, y simplemente compare lo que está usando internamente para el estado.

Respondido el 23 de Septiembre de 15 a las 21:09

Whooa, esto se ve genial. Lo comprobaré por mi uso y me pondré en contacto contigo. Marcus Hammarberg

Amigo, ¡eso funcionó muy bien! Vea mi uso aquí: github.com/marcushammarberg/SpecFlow.Assist.Dynamic . Exactamente lo que estaba buscando. Gracias - Marcus Hammarberg

Veo un método Equals para ExpandoObject .... como System.Dynamic.ExpandoObject.Equals (obj1, obj2); ¿Será esto suficiente? - zak

@zak lo que ves es solo el método Equals heredado de System.Object. Entonces, no, no comparará dos Expando's separados, y mucho menos, un Expando y otro objeto DLR. github.com/microsoft/referencesource/blob/master/System.Core/… - jbtule

Consulte "Enumerar y eliminar miembros" para obtener los miembros de un ExpandoObject http://msdn.microsoft.com/en-us/library/system.dynamic.expandoobject.aspx

Sin embargo, los objetos dinámicos arbitrarios no parecen exponer a los enumeradores.

Respondido 27 ago 11, 20:08

Gracias, mi pregunta no se formuló correctamente, pero la entendiste. Preguntaba sobre objetos dinámicos arbitrarios. - Marcus Hammarberg

Tienes que implementar IComparable-Interfaz. Luego, tiene las funciones apropiadas necesarias de .NET / C # para comparar dos objetos entre sí.

Respondido 27 ago 11, 20:08

Gran respuesta, pero mi pregunta era engañosa. Estaba tratando de comparar con objetos dinámicos. El hecho de que fueran ExpandoObject fue solo mi ejemplo de creación del objeto dinámico. Los objetos dinámicos arbitrarios no parecen ser capaces de hacer eso como respondió Raymond: Marcus Hammarberg

Los Objetos Expando se pueden utilizar como IDictonary<string, object> por lo que debería poder usar eso.

Algo como

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

Editar los AreEqual no funcionará

Pero podría intentar comparar los dos diccionarios de manera bastante simple.

Respondido 27 ago 11, 20:08

Gran respuesta, pero mi pregunta era engañosa. Estaba tratando de comparar con objetos dinámicos. El hecho de que fueran ExpandoObject fue solo mi ejemplo de creación del objeto dinámico. Los objetos dinámicos arbitrarios no parecen exponer a los enumeradores, como respondió Raymond: Marcus Hammarberg

También puede usar la biblioteca ObjectsComparer disponible en GitHub: ObjectsComparer

Esta biblioteca es un comparador de objeto a objeto que nos permite comparar objetos de forma recursiva miembro por miembro y definir reglas de comparación personalizadas para determinadas propiedades, campos o tipos. Admite enumerables (matrices, colecciones, listas), matrices multidimensionales, enumeraciones, banderas y objetos dinámicos (ExpandoObject, DynamicObject y objetos dinámicos generados por el compilador).

Dirígete a Valerii Tereshchenko excelente papel para más detalles.

Respondido el 13 de Septiembre de 20 a las 12:09

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.