比较 2 个对象并检索具有不同值的字段列表 [英] Comparing 2 objects and retrieve a list of fields with different values

查看:27
本文介绍了比较 2 个对象并检索具有不同值的字段列表的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

给定一个具有 35 个字段的类和 2 个具有一定数量不同字段值的对象.有没有一种聪明的方法来获取带有字段名称的列表,其中对象如下?

Given a class with 35 fields and 2 objects with a certain number of different fields value. Is there an clever way to get a list<String> with the fields name where the object are as following?

例如

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?

I see reflection as the way to go but with 35 fields I am afraid it's too heavy. Any other idea, like linq?

推荐答案

OK;这比我通常要做的疯狂要多得多,但这可能是一种有用的实用方法.它动态(缓存)创建 IL 来完成工作,处理值类型与引用类型对象、内置 IL 相等、相等运算符 (==) 和 EqualityComparer 其余的:

OK; this is insanely more effort than I would normally go to, but this might be a useful utility method. It creates IL on the fly (cached) to do the work, handling value-type vs ref-type objects, inbuilt IL equality, equality operators (==), and EqualityComparer<T> for the rest:

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

    }
}

这篇关于比较 2 个对象并检索具有不同值的字段列表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆