如何快速检查数据传输两个对象在C#中具有相同的属性?

我有这些数据传输对象:

public class Report { public int Id { get; set; } public int ProjectId { get; set; } //and so on for many, many properties. } 

我不想写

 public bool areEqual(Report a, Report b) { if (a.Id != b.Id) return false; if (a.ProjectId != b.ProjectId) return false; //Repeat ad nauseum return true; } 

有没有更快的方法来testing只有属性具有相同的值(不需要一行代码或每个属性一个逻辑expression式的对象)?

切换到结构不是一个选项。

如何反映,也许使用Expression.Compile()的性能? (注意这里的静态ctor确保我们每T只编译一次):

 using System; using System.Linq.Expressions; public class Report { public int Id { get; set; } public int ProjectId { get; set; } static void Main() { Report a = new Report { Id = 1, ProjectId = 13 }, b = new Report { Id = 1, ProjectId = 13 }, c = new Report { Id = 1, ProjectId = 12 }; Console.WriteLine(PropertyCompare.Equal(a, b)); Console.WriteLine(PropertyCompare.Equal(a, c)); } } static class PropertyCompare { public static bool Equal<T>(T x, T y) { return Cache<T>.Compare(x, y); } static class Cache<T> { internal static readonly Func<T, T, bool> Compare; static Cache() { var props = typeof(T).GetProperties(); if (props.Length == 0) { Compare = delegate { return true; }; return; } var x = Expression.Parameter(typeof(T), "x"); var y = Expression.Parameter(typeof(T), "y"); Expression body = null; for (int i = 0; i < props.Length; i++) { var propEqual = Expression.Equal( Expression.Property(x, props[i]), Expression.Property(y, props[i])); if (body == null) { body = propEqual; } else { body = Expression.AndAlso(body, propEqual); } } Compare = Expression.Lambda<Func<T, T, bool>>(body, x, y) .Compile(); } } } 

编辑:更新以处理字段:

 static class MemberCompare { public static bool Equal<T>(T x, T y) { return Cache<T>.Compare(x, y); } static class Cache<T> { internal static readonly Func<T, T, bool> Compare; static Cache() { var members = typeof(T).GetProperties( BindingFlags.Instance | BindingFlags.Public) .Cast<MemberInfo>().Concat(typeof(T).GetFields( BindingFlags.Instance | BindingFlags.Public) .Cast<MemberInfo>()); var x = Expression.Parameter(typeof(T), "x"); var y = Expression.Parameter(typeof(T), "y"); Expression body = null; foreach(var member in members) { Expression memberEqual; switch (member.MemberType) { case MemberTypes.Field: memberEqual = Expression.Equal( Expression.Field(x, (FieldInfo)member), Expression.Field(y, (FieldInfo)member)); break; case MemberTypes.Property: memberEqual = Expression.Equal( Expression.Property(x, (PropertyInfo)member), Expression.Property(y, (PropertyInfo)member)); break; default: throw new NotSupportedException( member.MemberType.ToString()); } if (body == null) { body = memberEqual; } else { body = Expression.AndAlso(body, memberEqual); } } if (body == null) { Compare = delegate { return true; }; } else { Compare = Expression.Lambda<Func<T, T, bool>>(body, x, y) .Compile(); } } } } 

最初回答( 问题1831747 )

看看我的MemberwiseEqualityComparer看看它是否符合你的需求。

它使用起来非常简单,效率也很高。 它使用IL-emit在第一次运行时生成整个Equals和GetHashCode函数(每个types使用一次)。 它将使用该types的默认相等比较器(EqualityComparer.Default)来比较给定对象的每个字段(私有或公共)。 我们已经在生产中使用它一段时间,它似乎稳定,但我不会保证=)

它处理所有那些在你自己的equals方法中你很less想到的边缘情况(例如,你不能把你自己的对象与null比较,除非你先把它装在一个对象中,然后把它closures更多与空相关的问题)。

我一直有意写一篇关于它的博客文章,但还没有得到它。 代码是有点没有文件,但如果你喜欢它,我可以把它清理一下。

 public override int GetHashCode() { return MemberwiseEqualityComparer<Foo>.Default.GetHashCode(this); } public override bool Equals(object obj) { if (obj == null) return false; return Equals(obj as Foo); } public override bool Equals(Foo other) { return MemberwiseEqualityComparer<Foo>.Default.Equals(this, other); } 

MemberwiseEqualityComparer是在麻省理工学院的许可证下发布的,你可以做任何你想做的事情,包括在专有的解决scheme中使用它,而不必改变你的授权。

我已经将Marc的代码扩展为我自己使用的完全IEqualityComparer实现,并且认为这对未来其他人可能是有用的:

 /// <summary> /// An <see cref="IEqualityComparer{T}"/> that compares the values of each public property. /// </summary> /// <typeparam name="T"> The type to compare. </typeparam> public class PropertyEqualityComparer<T> : IEqualityComparer<T> { // http://stackoverflow.com/questions/986572/hows-to-quick-check-if-data-transfer-two-objects-have-equal-properties-in-c/986617#986617 static class EqualityCache { internal static readonly Func<T, T, bool> Compare; static EqualityCache() { var props = typeof(T).GetProperties(); if (props.Length == 0) { Compare = delegate { return true; }; return; } var x = Expression.Parameter(typeof(T), "x"); var y = Expression.Parameter(typeof(T), "y"); Expression body = null; for (int i = 0; i < props.Length; i++) { var propEqual = Expression.Equal( Expression.Property(x, props[i]), Expression.Property(y, props[i])); if (body == null) { body = propEqual; } else { body = Expression.AndAlso(body, propEqual); } } Compare = Expression.Lambda<Func<T, T, bool>>(body, x, y).Compile(); } } /// <inheritdoc/> public bool Equals(T x, T y) { return EqualityCache.Compare(x, y); } static class HashCodeCache { internal static readonly Func<T, int> Hasher; static HashCodeCache() { var props = typeof(T).GetProperties(); if (props.Length == 0) { Hasher = delegate { return 0; }; return; } var x = Expression.Parameter(typeof(T), "x"); Expression body = null; for (int i = 0; i < props.Length; i++) { var prop = Expression.Property(x, props[i]); var type = props[i].PropertyType; var isNull = type.IsValueType ? (Expression)Expression.Constant(false, typeof(bool)) : Expression.Equal(prop, Expression.Constant(null, type)); var hashCodeFunc = type.GetMethod("GetHashCode", BindingFlags.Instance | BindingFlags.Public); var getHashCode = Expression.Call(prop, hashCodeFunc); var hashCode = Expression.Condition(isNull, Expression.Constant(0, typeof(int)), getHashCode); if (body == null) { body = hashCode; } else { body = Expression.ExclusiveOr(Expression.Multiply(body, Expression.Constant(typeof(T).AssemblyQualifiedName.GetHashCode(), typeof(int))), hashCode); } } Hasher = Expression.Lambda<Func<T, int>>(body, x).Compile(); } } /// <inheritdoc/> public int GetHashCode(T obj) { return HashCodeCache.Hasher(obj); } } 

不幸的是,你将不得不编写方法来比较字段值。 System.ValueType被构build为使用reflection并比较结构的字段值,但是由于性能低下,这也是不可取的。 最好的办法是重写Equals方法,并为强typesEquals重载实现IEquatable<T>接口。

当你在这个时候,你也可以提供一个好的GetHashCode覆盖,以补充Equals实现。 所有这些步骤都被认为是很好的做法。

你将需要使用reflection来做到这一点,请按照此链接 – > 比较对象属性在C#