2010-06-17 7 views
11

35 개의 필드와 특정 개수의 다른 필드 값을 가진 2 개의 개체가있는 클래스가 제공됩니다. 리스트를 얻는 영리한 방법이 있습니까? < String> 오브젝트가 다음과 같은 필드 이름을 사용합니까?2 개체 비교 및 ​​값이 다른 필드 목록 검색

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

목적 :

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

내가 갈 수있는 방법으로 반사를 볼 수 있지만 35 개 필드 나는 너무 무거운 두렵다. linq과 같은 다른 아이디어가 있습니까?

+1

반사의 비용을 : 그것은 나머지 심판 형 객체, 붙박이 IL 평등, 평등 사업자 대 값 형 (==), 및 EqualityComparer<T> 처리, 작업을 수행 할 비행 (캐시)에 IL를 생성 상대적입니다. 얼마나 자주 이러는거야? ** ** 이렇게하는 것이 더 빠르지 만, 단단한 루프로하지 않으면 리플렉션이 잘되어 있어야합니다 (간단합니다). –

+0

(예 : ILGenerator 또는 Expression을 통해이 코드를 작성할 수는 있지만 복잡성은 보증됩니까? 알려주십시오 ...) –

+0

Marc, WCF에서 처리하고 있지만 몇 명의 사용자에게 사용됩니다. 그래서 나는 반성하기로 결정했다. 대단히 노력해 주셔서 감사합니다. a – ajj

답변

1

반성은이 문제를 해결하는 방법이며 35 개의 필드가 문제라고 생각하지 않습니다.

(자신을 완전히 혼란스럽게 생각한 후에 나는이 문제에 대해 잘 이해하고 있다고 생각합니다.)

34

OK; 이것은 입니다. 미친 듯이보다 더 많은 노력을 기울이지 만 유용한 유틸리티 방법이 될 수 있습니다.

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

이것은 굉장합니다. 이 유틸리티를 재귀 적으로 만드는 것이 어렵습니까 (즉, 객체 그래프를 걷는 것)? :-) –

+0

@ 트로이 - 단일 항목의 경우 나쁘지 않습니다. 목록은 고통입니다. –