我想比较两个具有自定义类型的对象,并返回具有差异的数据。我在Equals
中覆盖了GetHashCode
和Address class
并实现了ValueComparer
,但是下面的代码返回了所有数据。请在下面查看预期结果。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp2
{
public class Person
{
public string FirstName { get; set; }
public string Lastname { get; set; }
public int Id { get; set; }
public List<Address> Address { get; set; }
}
public class Address:IEquatable<Address>
{
public string City { get; set; }
public string Country { get; set; }
public int Id { get; internal set; }
public int PersonId { get; set; }
public override bool Equals(object obj)
{
if (obj == null)
return false;
Address a = obj as Address;
if (object.ReferenceEquals(a, null))
return false;
return (a.City.Equals(City));
}
public virtual bool Equals(Address other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return other.City == City;
}
public override int GetHashCode()
{
return City.GetHashCode();
}
}
public class ValueComparer : IEqualityComparer<Person>
{
public int GetHashCode(Person co)
{
if (co == null)
{
return 0;
}
return co.FirstName.GetHashCode();
}
public bool Equals(Person x1, Person x2)
{
if (object.ReferenceEquals(x1, x2))
{
return true;
}
if (object.ReferenceEquals(x1, null) ||
object.ReferenceEquals(x2, null))
{
return false;
}
return x1.FirstName.Equals(x2.FirstName)
&& x1.Address.Equals(x2.Address);
}
}
class Program
{
static void Main(string[] args)
{
var personsOrig = new List<Person>
{
new Person {Id=1, FirstName = "John", Lastname = "Bill"
, Address=new List<Address>{new Address {Id=1, City = "New York", Country = "US", PersonId=1 }
,new Address {Id=2, City = "Okinawa", Country = "Japan", PersonId=1 }
},
}
,new Person {Id=2, FirstName = "Mary", Lastname = "Doe"
, Address=new List<Address>{new Address {Id=3, City = "Los Angeles", Country = "US", PersonId=2 }
} }
,new Person {Id=3, FirstName = "Joe", Lastname = "McDonalds"
, Address=new List<Address>{new Address {Id=4, City = "California", Country = "US", PersonId=3 }
} }
,new Person {Id=4, FirstName = "Donald", Lastname = "Gates"
, Address=new List<Address>{new Address {Id=5, City = "San Francisco", Country = "US", PersonId=4 }
} }
,new Person {Id=5, FirstName = "Shawn", Lastname = "Porter"
, Address=new List<Address>{new Address {Id=6, City = "Utah", Country = "US", PersonId=5 }
} }
};
var personsNew = new List<Person>
{
new Person {Id=1, FirstName = "John", Lastname = "Bill"
, Address=new List<Address>{
new Address { Id = 1, City = "New York", Country = "US", PersonId = 1 }
}
}
,new Person {Id=2, FirstName = "Mary", Lastname = "Doe"
, Address=new List<Address>{new Address {Id=7, City = "Florida", Country = "US", PersonId=2 }
} }
,new Person {Id=3, FirstName = "Joe", Lastname = "Jean"
, Address=new List<Address>{new Address {Id=4, City = "California", Country = "US", PersonId=3}
} }
,new Person {Id=4, FirstName = "Donald", Lastname = "Gates"
, Address=new List<Address>{new Address {Id=5, City = "San Francisco", Country = "US", PersonId=4 }
} }
,new Person {Id=5, FirstName = "Shawn", Lastname = "Porter"
, Address=new List<Address>{new Address {Id=6, City = "Utah", Country = "US", PersonId=5 }
} }
};
var xxx = personsNew.Except(personsOrig,new ValueComparer()).ToList();
}
}
}
预期产量
John Bill
Mary Doe
Joe McDonalds
答案 0 :(得分:1)
您正在比较Person对象的Address字段,该字段是Address对象的列表。因此,不会调用Address equals方法-而是在比较每个Person的地址列表(为了清楚起见,我建议将其重命名为lstAddresses),而无需建立任何逻辑来覆盖两个列表对象的默认比较。
答案 1 :(得分:0)
我最近不得不这样做,并实现了自定义的EqualityComparer。 它递归地检查属性及其值。
public class StructuralEqualityComparer : IEqualityComparer
{
private readonly Func<PropertyInfo, bool> _excludePredicate;
private readonly StructuralEqualityComparerOptions _options;
public StructuralEqualityComparer(Func<PropertyInfo, bool> excludePredicate = null, StructuralEqualityComparerOptions options = null)
{
Func<PropertyInfo, bool> defaultExcludePredicate = (propertyInfo) => false;
_excludePredicate = excludePredicate ?? defaultExcludePredicate;
_options = options;
}
public new bool Equals(object x, object y)
{
if (x == null)
return y == null;
if (y == null)
return false;
var bindingFlags = BindingFlags.Public | BindingFlags.Instance;
var xProperties = x.GetType().GetProperties(bindingFlags);
var xPropertiesToCompare = xProperties
.Except(xProperties.Where(_excludePredicate))
.OrderBy(propertyInfo => propertyInfo.Name);
var yProperties = y.GetType().GetProperties(bindingFlags);
var yPropertiesToCompare = yProperties
.Except(yProperties.Where(_excludePredicate))
.OrderBy(propertyInfo => propertyInfo.Name);
Func<object, object, bool> equalityPredicate = (value1, value2) => PropertyEquals(x, y, value1 as PropertyInfo, value2 as PropertyInfo);
return SequenceEquals(xPropertiesToCompare, yPropertiesToCompare, equalityPredicate);
}
private bool SequenceEquals(IEnumerable first, IEnumerable second, Func<object, object, bool> equalityPredicate)
{
var firstEnumerator = first.GetEnumerator();
var secondEnumerator = second.GetEnumerator();
{
while (firstEnumerator.MoveNext())
{
if (!secondEnumerator.MoveNext())
return false;
if (!equalityPredicate(firstEnumerator.Current, secondEnumerator.Current))
return false;
}
if (secondEnumerator.MoveNext())
return false;
}
return true;
}
public int GetHashCode(object obj) => obj.GetHashCode();
private bool PropertyEquals(object x, object y, PropertyInfo xPropertyInfo, PropertyInfo yPropertyInfo)
{
if (xPropertyInfo == null)
return yPropertyInfo == null;
if (yPropertyInfo == null)
return false;
if (_options.NameCompare)
{
if (xPropertyInfo.Name != yPropertyInfo.Name)
return false;
}
if (_options.TwoWayAssignableCompare)
{
if (!xPropertyInfo.PropertyType.IsAssignableFrom(yPropertyInfo.PropertyType) || !yPropertyInfo.PropertyType.IsAssignableFrom(xPropertyInfo.PropertyType))
return false;
}
else if (_options.OneWayAssignableCompare)
{
if (!xPropertyInfo.PropertyType.IsAssignableFrom(yPropertyInfo.PropertyType))
return false;
}
if (_options.ValueCompare)
{
var xValue = xPropertyInfo.GetValue(x);
var yValue = yPropertyInfo.GetValue(y);
if (xValue is IEnumerable || yValue is IEnumerable)
{
return SequenceEquals(xValue as IEnumerable, yValue as IEnumerable, _options.EqualityComparer.Equals);
}
if (_options.EqualityComparer.Equals(xValue, yValue) || _options.EqualityComparer.Equals(yValue, xValue))
return true;
if (_options.RecursiveCompare && (xValue != x || yValue != y))
{
return Equals(xValue, yValue);
}
}
return true;
}
}
public class StructuralEqualityComparerOptions
{
/// <summary>
/// Check that the names of the properties match
/// </summary>
public bool NameCompare { get; set; } = true;
/// <summary>
/// Check that the Type of TY can be assigned to TX.
/// </summary>
public bool OneWayAssignableCompare { get; set; } = true;
/// <summary>
/// Check that the Type of TY can be assigned to TX and vise versa, skips OneWayAssignableCompare.
/// </summary>
public bool TwoWayAssignableCompare { get; set; } = false;
/// <summary>
/// Check that the values of the properties match, using EqualityComparer
/// </summary>
public bool ValueCompare { get; set; } = true;
/// <summary>
/// Recursively walk for nested types
/// </summary>
public bool RecursiveCompare { get; set; } = true;
/// <summary>
/// The IEqualityComparer to use for ValueCompare
/// </summary>
public IEqualityComparer EqualityComparer { get; set; } = EqualityComparer<object>.Default;
}