C# 使用表达式树比较对象的所有属性
我正在尝试编写一个简单的生成器,它使用表达式树动态生成一个方法,该方法将一个类型实例的所有属性与该类型另一个实例的属性进行比较。这适用于大多数属性,如C# 使用表达式树比较对象的所有属性,c#,.net,expression-trees,dynamic-method,C#,.net,Expression Trees,Dynamic Method,我正在尝试编写一个简单的生成器,它使用表达式树动态生成一个方法,该方法将一个类型实例的所有属性与该类型另一个实例的属性进行比较。这适用于大多数属性,如int一个string,但不适用于DateTime?(可能还有其他可为空的值类型) 方法: static Delegate GenerateComparer(Type type) { var left = Expression.Parameter(type, "left"); var right = Expression.Parameter
int
一个string
,但不适用于DateTime?
(可能还有其他可为空的值类型)
方法:
static Delegate GenerateComparer(Type type)
{
var left = Expression.Parameter(type, "left");
var right = Expression.Parameter(type, "right");
Expression result = null;
foreach (var p in type.GetProperties())
{
var leftProperty = Expression.Property(left, p.Name);
var rightProperty = Expression.Property(right, p.Name);
var equals = p.PropertyType.GetMethod("Equals", new[] { p.PropertyType });
var callEqualsOnLeft = Expression.Call(leftProperty, equals, rightProperty);
result = result != null ? (Expression)Expression.And(result, callEqualsOnLeft) : (Expression)callEqualsOnLeft;
}
var method = Expression.Lambda(result, left, right).Compile();
return method;
}
在DateTime?
属性上,它会失败,原因是:
类型为'System.Nullable'1[System.DateTime]'的表达式不能用于方法'Boolean Equals(System.Object)'的'System.Object'类型的参数
好的,它会找到一个重载等于,该重载需要对象
。既然可以转换为对象,为什么我不能将日期时间?
传递给它呢?如果我看一下Nullable
,它确实覆盖了Equals(objecto)
PS:我意识到这不是一个合适的生成器,因为它不能处理null
值,但我要说的是:)
更新:Iraklis的答案确实适用于这个特定的问题,但最终我选择了一种更简单的方法,我认为这已经足够了:只需使用Expression.Equal
。我认为这涵盖了我99%的案例(不确定它是否能够在不覆盖=
的情况下处理覆盖=
,但这没关系) 如果使用以下代码检查类型是否可以为空,则可能会起作用:
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)){}
if(type.IsGenericType&&type.GetGenericTypeDefinition()==typeof(Nullable)){
代码示例来自
如果它们可以为空,那么您可以调用
Nullable.Equals<T>(T? n1, T? n2);
Nullable.Equals(T?n1,T?n2);
在网上搜索我可以使用的东西后,我决定自己也实现它。我没有使用表达式树。相反,我使用反射扫描所有属性,并使用ToString()
比较它们。如果属性是集合,它将比较集合中的每个元素是否相等
这是代码
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Linq;
namespace Utils
{
public class PropertyComparer<T> : IEqualityComparer<T>
{
public bool Equals(T x, T y)
{
IEnumerable<PropertyInfo> allProperties = typeof(T).GetProperties();
foreach(PropertyInfo pi in allProperties)
{
if (pi.GetCustomAttributes<EqualityIrrelevantAttribute>().Any())
{
continue;
}
object xProp = pi.GetValue(x);
object yProp = pi.GetValue(y);
if ((xProp == null) && (yProp == null))
{
continue;
}
else if ((xProp == null) || (yProp == null))
{
return false;
}
else if (xProp is ICollection)
{
if (!CollectionsEqual(xProp as ICollection, yProp as ICollection))
{
return false;
}
}
if (xProp.ToString() != yProp.ToString())
{
return false;
}
}
return true;
}
bool CollectionsEqual(ICollection left, ICollection right)
{
IEnumerator leftEnumerator = left.GetEnumerator();
IEnumerator rightEnumerator = right.GetEnumerator();
bool leftAdvanced = leftEnumerator.MoveNext();
bool rightAdvanced = rightEnumerator.MoveNext();
if ((leftAdvanced && !rightAdvanced) || (rightAdvanced && !leftAdvanced))
{
return false;
}
else if (!leftAdvanced && !rightAdvanced)
{
return true;
}
bool compareByClass = false;
object comparer = null;
MethodInfo equalsMethod = null;
// Inspect type first
object peek = leftEnumerator.Current;
Type valuesType = peek.GetType();
if (valuesType.IsClass)
{
compareByClass = true;
Type comparerType = typeof(PropertyComparer<>).MakeGenericType(new Type[] { valuesType });
equalsMethod = comparerType.GetMethod("Equals", new Type[] { valuesType, valuesType });
comparer = Activator.CreateInstance(comparerType);
}
leftEnumerator.Reset();
rightEnumerator.Reset();
while (true)
{
leftAdvanced = leftEnumerator.MoveNext();
rightAdvanced = rightEnumerator.MoveNext();
if ((leftAdvanced && !rightAdvanced) || (rightAdvanced && !leftAdvanced))
{
return false;
}
else if (!leftAdvanced && !rightAdvanced)
{
return true;
}
object leftValue = leftEnumerator.Current;
object rightValue = rightEnumerator.Current;
if (compareByClass)
{
bool result = (bool)equalsMethod.Invoke(comparer, new object[] { leftValue, rightValue });
if (!result)
{
return false;
}
}
else if (leftEnumerator.Current.ToString() != rightEnumerator.Current.ToString())
{
return false;
}
// Continue looping
}
}
public int GetHashCode(T obj)
{
throw new NotImplementedException();
}
}
}
使用系统;
使用系统集合;
使用System.Collections.Generic;
运用系统反思;
使用System.Linq;
命名空间Utils
{
公共类属性比较程序:IEqualityComparer
{
公共布尔等于(TX,TY)
{
IEnumerable allProperties=typeof(T).GetProperties();
foreach(所有属性中的PropertyInfo pi)
{
if(pi.GetCustomAttributes().Any())
{
继续;
}
对象xProp=pi.GetValue(x);
对象yProp=pi.GetValue(y);
if((xProp==null)和&(yProp==null))
{
继续;
}
else如果((xProp==null)| |(yProp==null))
{
返回false;
}
else if(xProp是ICollection)
{
如果(!CollectionsEqual(xProp作为ICollection,yProp作为ICollection))
{
返回false;
}
}
if(xProp.ToString()!=yProp.ToString())
{
返回false;
}
}
返回true;
}
布尔集合相等(ICollection left,ICollection right)
{
IEnumerator leftEnumerator=left.GetEnumerator();
IEnumerator rightEnumerator=right.GetEnumerator();
bool leftAdvanced=leftEnumerator.MoveNext();
boolRightAdvanced=rightEnumerator.MoveNext();
if((leftAdvanced&!righAdvanced)| |(righAdvanced&&!leftAdvanced))
{
返回false;
}
如果(!leftAdvanced&!rightAdvanced),则为else
{
返回true;
}
bool compareByClass=false;
对象比较器=null;
MethodInfo equalsMethod=null;
//先检查型号
object peek=leftEnumerator.Current;
Type valuesType=peek.GetType();
if(值类型IsClass)
{
compareByClass=true;
Type comparerType=typeof(PropertyComparer).MakeGenericType(新类型[]{valuesType});
equalsMethod=comparerType.GetMethod(“Equals”,新类型[]{valuesType,valuesType});
comparer=Activator.CreateInstance(comparerType);
}
leftEnumerator.Reset();
rightEnumerator.Reset();
while(true)
{
leftAdvanced=leftEnumerator.MoveNext();
rightAdvanced=rightEnumerator.MoveNext();
if((leftAdvanced&!righAdvanced)| |(righAdvanced&&!leftAdvanced))
{
返回false;
}
如果(!leftAdvanced&!rightAdvanced),则为else
{
返回true;
}
对象leftValue=leftEnumerator.Current;
对象rightValue=rightEnumerator.Current;
如果(比较周期类)
{
bool result=(bool)equalsMethod.Invoke(比较器,新对象[]{leftValue,rightValue});
如果(!结果)
{
返回false;
}
}
else if(leftEnumerator.Current.ToString()!=rightEnumerator.Current.ToString())
{
返回false;
}
//继续循环
}
}
公共int GetHashCode(T obj)
{
抛出新的NotImplementedException();
}
}
}
如果一个类的属性本身就是一个类,那么它将创建一个新的比较器来比较