比较2个对象并检索具有不同值的字段列表

时间:2010-06-17 09:26:55

标签: c# linq

给定一个包含35个字段的类和2个具有一定数量的不同字段值的对象。 是否有一种聪明的方法来获取列表< String>使用字段名称,对象如下所示?

e.g。

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?

2 个答案:

答案 0 :(得分:35)

行;这是疯狂比我通常要做的更多的努力,但这可能是一个有用的实用方法。它动态创建IL(缓存)来完成工作,处理值类型vs ref类型对象,内置IL等式,等式运算符(==)和其余EqualityComparer<T>

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 :(得分:1)

反思就是采用这种方式,我不认为35个字段是个问题。

(在完全迷惑自己之后,我回过头来以为我理解这个问题并且反思对此很好)。