我有这些数据传输对象:
public class Report
{
public int Id { get; set; }
public int ProjectId { get; set; }
//and so on for many, many properties.
}
Run Code Online (Sandbox Code Playgroud)
我不想写
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;
}
Run Code Online (Sandbox Code Playgroud)
有没有更快的方法来测试两个只有属性的对象是否具有相同的值(每个属性不需要一行代码或一个逻辑表达式?)
切换到结构不是一种选择.
Mar*_*ell 66
一些反思,或许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();
}
}
}
Run Code Online (Sandbox Code Playgroud)
编辑:更新以处理字段:
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();
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
最初回答于(问题 1831747)
查看我的MemberwiseEqualityComparer,看看它是否符合您的需求。
它真的很容易使用,而且效率也很高。它使用 IL-emit 在第一次运行时生成整个 Equals 和 GetHashCode 函数(对于使用的每种类型一次)。它将使用该类型的默认相等比较器 (EqualityComparer.Default) 比较给定对象的每个字段(私有或公共)。我们已经在生产中使用它一段时间了,它看起来很稳定,但我不做任何保证 =)
它处理了所有那些在滚动自己的 equals 方法时很少想到的麻烦的边缘情况(即,您不能将自己的对象与 null 进行比较,除非您首先将它装箱到一个对象中并且很多更多与空值相关的问题)。
我一直想写一篇关于它的博客文章,但还没有开始。代码有点没有记录,但如果你喜欢它,我可以稍微清理一下。
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);
}
Run Code Online (Sandbox Code Playgroud)
MemberwiseEqualityComparer 是在MIT 许可下发布的,这意味着您几乎可以用它做任何您想做的事情,包括在专有解决方案中使用它,而无需稍微改变您的许可。