我有一个方法可以比较共享相同接口的2个类的属性。 涉及到许多属性而不是检查每个属性,我宁愿迭代它们,从而节省了重复编码。 我认为用反射做这个可能是可能的,但我无法弄清楚如何。 这段代码失败了,因为我不明白我在做什么。
public void IsEqual(IPropertyComparer src, IPropertyComparer property)
{
PropertyInfo[] srcPI = src.GetType().GetPublicProperties();
PropertyInfo[] propertyPI = property.GetType().GetPublicProperties();
var numberOfProperties = srcPI.Count();
for (var i = 0; i < numberOfProperties; i++)
{
var srcValue = srcPI[i].GetValue(src, null);
var propertyValue = propertyPI[i].GetValue(property, null);
var propertyName = srcPI[i].Name;
if(srcValue.Equals(propertyValue) == false)
{
this.Action = "ValidateExistingPropertyFailOn" + propertyName;
this.Data.Error = true;
this.Data.ErrorMessage =
string.Format(this.Template, this.UPRN, propertyName, srcValue, propertyValue);
return;
}
}
代码中有一个扩展方法。扩展方法失败,因为Type未被识别为接口;
public static PropertyInfo[] GetPublicProperties(this Type type)
{
if (type.IsInterface)
{
var propertyInfos = new List<PropertyInfo>();
var considered = new List<Type>();
var queue = new Queue<Type>();
considered.Add(type);
queue.Enqueue(type);
while (queue.Count > 0)
{
var subType = queue.Dequeue();
foreach (var subInterface in subType.GetInterfaces())
{
if (considered.Contains(subInterface))
{
continue;
}
considered.Add(subInterface);
queue.Enqueue(subInterface);
}
var typeProperties = subType.GetProperties(
BindingFlags.FlattenHierarchy
| BindingFlags.Public
| BindingFlags.Instance);
var newPropertyInfos = typeProperties
.Where(x => !propertyInfos.Contains(x));
propertyInfos.InsertRange(0, newPropertyInfos);
}
return propertyInfos.ToArray();
}
return type.GetProperties(BindingFlags.FlattenHierarchy
| BindingFlags.Public | BindingFlags.Instance);
}
答案 0 :(得分:1)
如果我理解正确,你会尝试比较两个共同实现接口的对象,并且只比较接口中定义的属性进行比较。
如果是这种情况,请尝试:
<强> EDITED 强>
为防止出现空异常,我编辑了代码:
bool IsEqual(IPropertyComparer o1, IPropertyComparer o2)
{
var props = typeof(IPropertyComparer).GetProperties();
foreach(var prop in props)
{
var v1 = prop.GetValue(o1);
var v2 = prop.GetValue(o2);
if(v1 == null)
{
if(v2 != null) return false;
}
else
{
if(!v1.Equals(v2)) return false;
}
}
return true;
}
答案 1 :(得分:1)
如果您对比较两个对象实例的需求变得更加复杂,那么您应该在NuGet上查看这个包:
https://www.nuget.org/packages/CompareNETObjects/
和codeplex页面:
http://comparenetobjects.codeplex.com/
非常擅长给你一个&#34;差异&#34;,有时可能是你真正想要的......
更新 - 基于对象内的字段检查Equality的通用,可重用的方法。
我偶然发现了吉米·博加德写的ValueObject<T>
课程,这可能引起一些人的兴趣。它的最初目的非常适合于DDD,其中相同属性的对象被认为是相同的,无论它们的基于引用类型的哈希码如何。
http://grabbagoft.blogspot.com/2007/06/generic-value-object-equality.html
这是一个稍微更新的版本,使用Linq来提高可读性和处理前置条件检查的更好方法 - 例如x.ForNull(nameof(firstInCompare));
public abstract class ValueObject<T> : IEquatable<T> where T : ValueObject<T>
{
public override bool Equals(object obj)
{
if (obj == null)
return false;
var other = obj as T;
return Equals(other);
}
public override int GetHashCode()
{
var fields = GetFields();
const int startValue = 17;
const int multiplier = 59;
return fields
.Select(field => field.GetValue(this))
.Where(value => value != null)
.Aggregate(startValue, (current, value) => current * multiplier + value.GetHashCode());
}
public virtual bool Equals(T other)
{
if (other == null)
return false;
var t = GetType();
var otherType = other.GetType();
if (t != otherType)
return false;
var fields = t.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
foreach (var field in fields)
{
var value1 = field.GetValue(other);
var value2 = field.GetValue(this);
if (value1 == null)
{
if (value2 != null)
return false;
}
else if (!value1.Equals(value2))
return false;
}
return true;
}
private IEnumerable<FieldInfo> GetFields()
{
var t = GetType();
t.ForNull("this");
var fields = new List<FieldInfo>();
while (t != typeof(object))
{
if (t == null) continue;
fields.AddRange(t.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public));
t = t.BaseType;
}
return fields;
}
public static bool operator ==(ValueObject<T> firstInCompare, ValueObject<T> secondInCompare)
{
firstInCompare.ForNull(nameof(firstInCompare));
secondInCompare.ForNull(nameof(secondInCompare));
return firstInCompare?.Equals(secondInCompare) ?? false;
}
public static bool operator !=(ValueObject<T> firstInCompare, ValueObject<T> secondInCompare)
{
return !(firstInCompare == secondInCompare);
}
}
预处理防护可以在像这样的静态助手类中找到
public static class Guard
{
public static void ForLessEqualZero(this int value, string parameterName)
{
if (value <= 0)
{
throw new ArgumentOutOfRangeException(parameterName);
}
}
public static void ForPrecedesDate(this DateTime value, DateTime dateToPrecede, string parameterName)
{
if (value >= dateToPrecede)
{
throw new ArgumentOutOfRangeException(parameterName);
}
}
public static void ForNullOrEmpty(this string value, string parameterName)
{
if (string.IsNullOrEmpty(value))
{
throw new ArgumentOutOfRangeException(parameterName);
}
}
public static void ForNull<T>(this T value, string parameterName)
{
ForValueType<T>(parameterName);
if (value == null)
{
throw new ArgumentNullException(parameterName);
}
}
private static void ForValueType<T>(string parameterName)
{
if (typeof(T).IsValueType)
{
throw new ArgumentException("parameter should be reference type, not value type", parameterName);
}
}
}
要以通用,可重复使用的方式检查相等性,可以将对象包装在ValueObject<T>
中。
或者您可以窃取实施并以更具体的方式执行。
答案 2 :(得分:0)
我前段时间一直在找这样的东西,最后设法编写了这个功能。它也适用于您的界面。
bool IsEqual(object obj)
{
var type = this.GetType();
bool SameObj = true;
//for each public property from your class
type.GetProperties().ToList().ForEach(prop =>
{
//dynamically checks there equals
if (!prop.GetValue(this, null).Equals(prop.GetValue(obj, null)))
{
SameObj = false;
}
});
return SameObj;
}