题
我有这些数据转让的对象:
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;
}
有没有更快的方法来测试,如果两个物体只有性质具有相同的价值观(东西,不要求一行代码或一个合乎逻辑的表达每个酒店?)
切换到结构不是一个选项。
解决方案
怎么样一些反思,可能使用Expression.Compile()
性能? (注意此处的静态构造函数可确保我们只每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-发生成的整个平等和GetHashCode功能上的第一次运行(一旦为每一类使用)。它会比较各个领域(私人或公共)的给予对象,使用默认的平等。对于这类型(EqualityComparer.默认)。我们已经使它在生产,而且它似乎是稳定的,但我会离开不能保证=)
它需要照顾的所有这些pescy边缘的情况下,很少想到时你自己的平等方法(即,你不能。你自己的目的与空除非你已经装箱,它在一个目的第一和很多更多的空有关的问题)。
我一直都想写一篇博客,但还没有得到解决它。代码是一位非法,但是如果你喜欢,我可以清理它一点。
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被释放下 麻省理工学院的许可 meaining你可以做很多你想要什么有了它,其中包括使用专利解决方案,而不改变你的许可一点。
我已经扩展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
是建立在使用反射和比较struct
的字段值,但即使这是不妥当的,由于性能下降。做的最好的事情是重写Equals
方法,也实现 IEquatable<T>
一>接口,用于强类型Equals
过载。
当你在这,你可能也提供了良好的GetHashCode
覆盖以及补充Equals
实施。所有这些步骤都被认为好的做法。
您将需要使用反射来做到这一点,请点击此链接 - > 比较对象在c#属性