2010-06-17 8 views
11

Dada una clase con 35 campos y 2 objetos con un cierto número de campos diferentes de valor. ¿Existe alguna manera inteligente de obtener una lista < String> con el nombre de los campos donde se encuentran los objetos?Comparando 2 objetos y recuperando una lista de campos con valores diferentes

p. Ej.

obj1.Name = "aaa"; 
obj1.LastName = "bbb"; 
obj1.Address = "xcs"; 
obj2.Name = "aaa"; 
obj2.LastName = "ccc"; 
obj2.Address = "jk"; 

objetivo:

list<<String>String> containing 2 Strings LastName and Address 

veo la reflexión como el camino a seguir, pero con 35 campos me temo que sea demasiado pesada. ¿Alguna otra idea, como linq?

+1

El costo de la reflexión es relativo. ¿Con qué frecuencia estás haciendo esto? Hay ** formas ** de hacerlo mucho más rápido, pero si no lo haces en un circuito cerrado, la reflexión debería estar bien (y es simple). –

+0

(por ejemplo, sería posible escribir esto mediante ILGenerator o Expression, pero ¿está justificada la complejidad? Hágamelo saber ...) –

+0

Marc, lo estoy haciendo dentro de WCF, pero se usará para un par de usuarios, así que decidí reflexionar. Muchas gracias por sus esfuerzos. a – ajj

Respuesta

1

La reflexión es el camino a seguir con esto y no creo que 35 campos sea un problema.

(Después de confundirme por completo, vuelvo a pensar que entiendo que la pregunta y la reflexión estarían bien para esto).

34

OK; esto es increíblemente más esfuerzo de lo que normalmente haría, pero este podría ser un método de utilidad útil. Se crea IL sobre la marcha (en caché) para hacer el trabajo, manipulación de valor de tipo vs objetos de tipo ref, la igualdad IL incorporado, operadores de igualdad (==), y EqualityComparer<T> para el resto:

using System.Collections.Generic; 
using System.Reflection; 
using System.Reflection.Emit; 

namespace ConsoleApplication2 
{ 
    using System; 
    class Program 
    { 
     static void Main() 
     { 
      WriteDeltas(new Foo {X = 123, Y = DateTime.Today, Z = null}, 
         new Foo {X = 124, Y = DateTime.Today, Z = null}); 
      WriteDeltas(new Foo { X = 123, Y = DateTime.Today, Z = null }, 
         new Foo { X = 123, Y = DateTime.Now, Z = new Dummy()}); 
      WriteDeltas(new Bar { X = 123, Y = DateTime.Today, Z = null }, 
         new Bar { X = 124, Y = DateTime.Today, Z = null }); 
      WriteDeltas(new Bar { X = 123, Y = DateTime.Today, Z = null }, 
         new Bar { X = 123, Y = DateTime.Now, Z = new Dummy() }); 
     } 
     static void WriteDeltas<T>(T x, T y) 
     { 
      Console.WriteLine("----"); 
      foreach(string delta in PropertyComparer<T>.GetDeltas(x,y)) 
      { 
       Console.WriteLine(delta); 
      } 

     } 

    } 
    class Dummy {} 
    class Foo 
    { 
     public int X { get; set; } 
     public DateTime Y { get; set; } 
     public Dummy Z { get; set; } 
    } 
    struct Bar 
    { 
     public int X { get; set; } 
     public DateTime Y { get; set; } 
     public Dummy Z { get; set; } 
    } 

    public static class PropertyComparer<T> 
    { 
     private static readonly Func<T, T, List<string>> getDeltas; 
     static PropertyComparer() 
     { 
      var dyn = new DynamicMethod(":getDeltas", typeof (List<string>), new[] {typeof (T), typeof (T)},typeof(T)); 
      var il = dyn.GetILGenerator(); 
      il.Emit(OpCodes.Newobj, typeof (List<string>).GetConstructor(Type.EmptyTypes)); 
      bool isValueType = typeof (T).IsValueType; 
      OpCode callType = isValueType ? OpCodes.Call : OpCodes.Callvirt; 
      var add = typeof(List<string>).GetMethod("Add"); 
      foreach (var prop in typeof(T).GetProperties()) 
      { 
       if (!prop.CanRead) continue; 
       Label next = il.DefineLabel(); 
       switch (Type.GetTypeCode(prop.PropertyType)) 
       { 
        case TypeCode.Boolean: 
        case TypeCode.Byte: 
        case TypeCode.Char: 
        case TypeCode.Double: 
        case TypeCode.Int16: 
        case TypeCode.Int32: 
        case TypeCode.Int64: 
        case TypeCode.SByte: 
        case TypeCode.Single: 
        case TypeCode.UInt16: 
        case TypeCode.UInt32: 
        case TypeCode.UInt64: 
         if(isValueType) {il.Emit(OpCodes.Ldarga_S, (byte)0);} else {il.Emit(OpCodes.Ldarg_0);} 
         il.EmitCall(callType, prop.GetGetMethod(), null); 
         if (isValueType) { il.Emit(OpCodes.Ldarga_S, (byte)1); } else { il.Emit(OpCodes.Ldarg_1); } 
         il.EmitCall(callType, prop.GetGetMethod(), null); 
         il.Emit(OpCodes.Ceq); 
         break; 
        default: 
         var pp = new Type[] {prop.PropertyType, prop.PropertyType}; 
         var eq = prop.PropertyType.GetMethod("op_Equality", BindingFlags.Public | BindingFlags.Static, null, pp, null); 
         if (eq != null) 
         { 
          if (isValueType) { il.Emit(OpCodes.Ldarga_S, (byte)0); } else { il.Emit(OpCodes.Ldarg_0); } 
          il.EmitCall(callType, prop.GetGetMethod(), null); 
          if (isValueType) { il.Emit(OpCodes.Ldarga_S, (byte)1); } else { il.Emit(OpCodes.Ldarg_1); } 
          il.EmitCall(callType, prop.GetGetMethod(), null); 
          il.EmitCall(OpCodes.Call, eq, null); 

         } 
         else 
         { 
          il.EmitCall(OpCodes.Call, typeof(EqualityComparer<>).MakeGenericType(prop.PropertyType).GetProperty("Default").GetGetMethod(), null); 
          if (isValueType) { il.Emit(OpCodes.Ldarga_S, (byte)0); } else { il.Emit(OpCodes.Ldarg_0); } 
          il.EmitCall(callType, prop.GetGetMethod(), null); 
          if (isValueType) { il.Emit(OpCodes.Ldarga_S, (byte)1); } else { il.Emit(OpCodes.Ldarg_1); } 
          il.EmitCall(callType, prop.GetGetMethod(), null); 
          il.EmitCall(OpCodes.Callvirt, typeof(EqualityComparer<>).MakeGenericType(prop.PropertyType).GetMethod("Equals", pp), null); 
         } 
         break; 
       } 
       il.Emit(OpCodes.Brtrue_S, next); // equal 
       il.Emit(OpCodes.Dup); 
       il.Emit(OpCodes.Ldstr, prop.Name); 
       il.EmitCall(OpCodes.Callvirt, add, null); 
       il.MarkLabel(next); 
      } 
      il.Emit(OpCodes.Ret); 
      getDeltas = (Func<T, T, List<string>>)dyn.CreateDelegate(typeof (Func<T, T, List<string>>)); 
     } 
     public static List<string> GetDeltas(T x, T y) { return getDeltas(x, y); } 

    } 
} 
+1

Esto es asombroso. ¿Sería difícil hacer que esta utilidad sea recursiva (es decir, caminar el gráfico de objetos)? :-) –

+0

@Troy: para artículos individuales, no está mal. Las listas son un dolor. –

Cuestiones relacionadas