比较泛型方法中的列表/IEnumerable 类型属性
本文关键字:IEnumerable 类型 属性 列表 泛型方法 比较 | 更新日期: 2023-09-27 17:57:02
我正在尝试比较包含List<>
类型属性的对象。我能够比较简单的属性,但卡在复杂的属性上。
foreach (PropertyInfo pi in properties)
{
object oldValue = pi.GetValue(oldObject), newValue = pi.GetValue(newObject);
if (pi.PropertyType.IsGenericType && typeof(IEnumerable).IsAssignableFrom(pi.PropertyType))
{
Type type = oldValue.GetType().GetGenericArguments()[0];
/* Need something like below commented line.*/
// var added = newValue.Except(oldValue)
// var removed = oldValue.Except(newValue);
}}
在 if 块中,我需要在列表类型属性中找到添加和删除的对象。在对象中,我们有键属性属性来查找添加和删除的对象。
好吧,根据我对这个问题的理解,这是完整的解决方案。
下面是指定项的键属性的键属性:
[AttributeUsage(AttributeTargets.Property)]
public class KeyAttribute : Attribute
{
}
对于测试,假设我们有一个名为 SomeClass
的类,其中包含一个 List<>
属性,以及一个名为 SomeItem
的项类,其中包含一个键属性,以及通过比较忽略的其他属性:
public class SomeClass
{
public List<SomeItem> Items { get; set; }
}
public class SomeItem
{
[Key]
public int TheKey { get; set; }
public string SomeValue { get; set; }
}
以下是执行比较的函数:
public void CompareNewWithOld(object oldObject, object newObject, List<object> added, List<object> removed)
{
var properties = typeof (SomeClass).GetProperties();
foreach (PropertyInfo pi in properties)
{
object oldValue = pi.GetValue(oldObject), newValue = pi.GetValue(newObject);
if (pi.PropertyType.IsGenericType && typeof(IEnumerable).IsAssignableFrom(pi.PropertyType))
{
var itemType = pi.PropertyType.GetGenericArguments()[0];
var itemKeyProperty = itemType
.GetProperties()
.FirstOrDefault(ipi => ipi.GetCustomAttribute<KeyAttribute>() != null);
if (itemKeyProperty == null)
{
continue; // no Key property -- cannot compare
}
var comparer = new ItemByKeyEqualityComparer(itemKeyProperty);
HashSet<object> oldSet = new HashSet<object>(((IEnumerable)oldValue).Cast<object>(), comparer);
HashSet<object> newSet = new HashSet<object>(((IEnumerable)newValue).Cast<object>(), comparer);
HashSet<object> removedSet = new HashSet<object>(oldSet, comparer);
removedSet.ExceptWith(newSet);
HashSet<object> addedSet = new HashSet<object>(newSet, comparer);
addedSet.ExceptWith(oldSet);
added.AddRange(addedSet);
removed.AddRange(removedSet);
}
}
}
为了方便地通过键属性与 HashSet<T>
比较项目对象,我们还需要实现一个相等比较器类,如下所示:
public class ItemByKeyEqualityComparer : IEqualityComparer<object>
{
private readonly PropertyInfo _keyProperty;
public ItemByKeyEqualityComparer(PropertyInfo keyProperty)
{
_keyProperty = keyProperty;
}
public bool Equals(object x, object y)
{
var kx = _keyProperty.GetValue(x);
var ky = _keyProperty.GetValue(y);
if (kx == null)
{
return (ky == null);
}
return kx.Equals(ky);
}
public int GetHashCode(object obj)
{
var key = _keyProperty.GetValue(obj);
return (key == null ? 0 : key.GetHashCode());
}
}
这是一个通过的测试:
[Test]
public void TestCompareNewWithOld()
{
var oldObject = new SomeClass() {
Items = new List<SomeItem>() {
new SomeItem() { TheKey = 1, SomeValue = "A"},
new SomeItem() { TheKey = 2, SomeValue = "B"},
new SomeItem() { TheKey = 3, SomeValue = "C"},
new SomeItem() { TheKey = 4, SomeValue = "D"},
}
};
var newObject = new SomeClass() {
Items = new List<SomeItem>() {
new SomeItem() { TheKey = 3, SomeValue = "W"},
new SomeItem() { TheKey = 4, SomeValue = "V"},
new SomeItem() { TheKey = 5, SomeValue = "U"},
new SomeItem() { TheKey = 6, SomeValue = "T"},
}
};
var added = new List<object>();
var removed = new List<object>();
CompareNewWithOld(oldObject, newObject, added, removed);
Assert.That(removed, Is.EquivalentTo(new[] {
oldObject.Items[0], //A
oldObject.Items[1] //B
}));
Assert.That(added, Is.EquivalentTo(new[] {
newObject.Items[2], //U
newObject.Items[3] //T
}));
}
将oldValue
和newValue
转换为IEnumerable<Object>
,然后根据需要比较它们:
if (IsGenericEnumerable(pi)) {
IEnumerable<Object> newEnumerable = (IEnumerable<Object>) newValue;
IEnumerable<Object> oldEnumerable = (IEnumerable<Object>) oldValue;
// operate with newEnumerable and oldEnumerable as needed by the logic
// ...
}