2010-06-17 14 views
11

Podano klasę z 35 polami i 2 obiektami o pewnej liczbie różnych wartości pól. Czy istnieje sprytny sposób na uzyskanie listy < Ciąg> z nazwą pola, w którym obiekt wygląda następująco?Porównywanie 2 obiektów i pobieranie listy zmiennych o różnych wartościach

np.

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

cel:

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

widzę odbicie jak do zrobienia, ale z 35 pól Obawiam się, że jest zbyt ciężki. Jakieś inne pomysły, takie jak linq?

+1

koszt refleksji jest względny. Jak często to robisz? Tam ** są ** sposoby, aby zrobić to znacznie szybciej, ale jeśli nie robisz tego w ciasnej pętli, odbicie powinno być w porządku (i jest proste). –

+0

(na przykład, byłoby to możliwe za pomocą ILGenerator lub Expression - ale jest to złożoność uzasadniona? Daj mi znać ...) –

+0

Marc, robię to w WCF, ale będzie on używany dla kilku użytkowników, więc zdecydowałem się na refleksję. Bardzo Ci dziękuję za wysiłek. a – ajj

Odpowiedz

1

Refleksja jest drogą do tego i nie uważam, że 35 pól to problem.

(Po całkowitym pomieszaniu się wracam do myślenia, że ​​rozumiem, że pytanie i refleksja byłyby w porządku).

34

OK; to jest znacznie więcej wysiłku niż zwykle, ale może to być przydatna metoda użyteczności. Tworzy IL w locie (kopia) do pracy, obsługi Wartość typ vs obiektów ref typu, wbudowany równości IL, operatorów równości (==) i EqualityComparer<T> dla reszty:

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

To jest niesamowite. Czy trudno byłoby uczynić to narzędzie rekursywnym (tj .: chodzenie po wykresie obiektów)? :-) –

+0

@ Troy - dla pojedynczych przedmiotów, nieźle. Listy to ból. –

Powiązane problemy