如何快速检查两个数据传输对象在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; } 

有没有更快的方法来测试两个只有属性的对象是否具有相同的值(每个属性不需要一行代码或一个逻辑表达式?)

切换到结构不是一种选择。

一些reflection怎么样,也许使用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 x, T y) { return Cache.Compare(x, y); } static class Cache { internal static readonly Func 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>(body, x, y) .Compile(); } } } 

编辑:更新以处理字段:

 static class MemberCompare { public static bool Equal(T x, T y) { return Cache.Compare(x, y); } static class Cache { internal static readonly Func Compare; static Cache() { var members = typeof(T).GetProperties( BindingFlags.Instance | BindingFlags.Public) .Cast().Concat(typeof(T).GetFields( BindingFlags.Instance | BindingFlags.Public) .Cast()); 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>(body, x, y) .Compile(); } } } } 

最初回答( 问题1831747 )

查看我的MemberwiseEqualityComparer ,看看它是否符合您的需求。

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

它会处理你在滚动自己的equals方法时很少想到的所有那些pescy edge-cases(也就是说,你不能将你自己的对象与null进行比较,除非你先把它放在一个对象中并且很多都是关闭的更多与空相关的问题)。

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

 public override int GetHashCode() { return MemberwiseEqualityComparer.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.Default.Equals(this, other); } 

MemberwiseEqualityComparer是根据MIT许可证发布的,你可以随意使用它,包括在专有解决方案中使用它,而不会改变你的许可。

我已经将Marc的代码扩展为一个完全成熟的IEqualityComparer实现,用于我自己的用途,并认为这对未来的其他人可能有用:

 ///  /// An  that compares the values of each public property. ///  ///  The type to compare.  public class PropertyEqualityComparer : IEqualityComparer { // 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 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>(body, x, y).Compile(); } } ///  public bool Equals(T x, T y) { return EqualityCache.Compare(x, y); } static class HashCodeCache { internal static readonly Func 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>(body, x).Compile(); } } ///  public int GetHashCode(T obj) { return HashCodeCache.Hasher(obj); } } 

不幸的是,您将不得不编写比较字段值的方法。 构建System.ValueType是为了使用reflection并比较结构的字段值,但由于性能低下,这甚至是不可取的。 最好的做法是覆盖Equals方法,并为强类型Equals重载实现IEquatable接口。

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

你需要使用reflection来做到这一点,请点击此链接 – > 比较c#中的对象属性