假设我有以下类(class):
public class TsvDataModel : IEquatable<TsvDataModel>
{
public int ElementId { get; set; }
public string Location { get; set; }
public string RoomKey { get; set;
public decimal Area { get; set; }
public bool Equals(TsvDataModel other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return ElementUid == other.ElementUid && Location == other.Location && RoomKey == other.RoomKey &&
SubType == other.SubType;
}
// current equality comparison implementation
public override bool Equals(object obj) => ReferenceEquals(this, obj) || (obj is TsvDataModel other && Equals(other));
public override int GetHashCode() => HashCode.Combine(ElementUid, Location, RoomKey, SubType);
}
我想要的是根据用户提供的属性创建动态相等性,即有时我只想通过 ElementId 进行比较,有时想包含/排除其他属性。我不想创建所有可能的组合。我尝试使用反射通过 dynamic
获取属性type ,我可以获取值和类型,但比较不起作用。
我更愿意将属性作为属性名称(字符串)列表传递。
我想使用 IEqualityComparer
或IEquitable
(我将此对象用作 HashSet<T>
)。
最佳答案
这是一个DynamicEqualityComparer
:
public class DynamicEqualityComparer<T> : IEqualityComparer<T>
{
private readonly IEnumerable<Func<T, object>> _propertySelectors;
public DynamicEqualityComparer(params Func<T, object>[] propertySelectors)
{
_propertySelectors = propertySelectors;
}
public bool Equals(T x, T y)
{
return _propertySelectors.All(propertySelector =>
object.Equals(propertySelector(x), propertySelector(y)));
}
public int GetHashCode(T obj)
{
var hashCode = new HashCode();
foreach (var propertySelector in _propertySelectors)
{
hashCode.Add(propertySelector((T)obj));
}
return hashCode.ToHashCode();
}
public static DynamicEqualityComparer<T> FromPropertyNames(IEnumerable<string> propertyNames)
{
IEnumerable<Func<T, object>> selectors = propertyNames.Select(propertyName =>
{
var property = typeof(T).GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public);
if (property == null)
{
throw new Exception($"Unable to find a property named {propertyName} in type {typeof(T)}");
}
return new Func<T, object>(obj => property.GetValue(obj));
});
return new DynamicEqualityComparer<T>(selectors.ToArray());
}
}
用法:
var comparer = new DynamicEqualityComparer<TsvDataModel>(
model => model.Location,
model => model.RoomKey,
model => model.Area);
var hashset = new HashSet<TsvDataModel>(comparer);
或
var comparer = DynamicEqualityComparer<TsvDataModel>
.FromPropertyNames(new[] {"Location", "RoomKey", "Area"});
var hashset = new HashSet<TsvDataModel>(comparer);
为了更好地衡量,这里有一些单元测试:
[Fact]
public void Recognizes_Equal_Models()
{
var modelA = new TsvDataModel { ElementId = 2, Location = "x", RoomKey = "y", Area = 1.1m };
var modelB = new TsvDataModel { ElementId = 3, Location = "x", RoomKey = "y", Area = 1.1m };
var comparer = DynamicEqualityComparer<TsvDataModel>
.FromPropertyNames(new[] { "Location", "RoomKey", "Area" });
var hashset = new HashSet<TsvDataModel>(comparer);
hashset.Add(modelA);
hashset.Add(modelB);
// ElementId is ignored. Location, RoomKey, and Area are the same.
// So the comparer indicates that they are "equal."
Assert.Equal(1, hashset.Count);
}
[Fact]
public void Recognizes_Unequal_Models()
{
var modelA = new TsvDataModel { ElementId = 2, Location = "xy", RoomKey = "y", Area = 1.1m };
var modelB = new TsvDataModel { ElementId = 3, Location = "x", RoomKey = "y", Area = 1.1m };
var comparer = DynamicEqualityComparer<TsvDataModel>
.FromPropertyNames(new[] { "Location", "RoomKey", "Area" });
var hashset = new HashSet<TsvDataModel>(comparer);
hashset.Add(modelA);
hashset.Add(modelB);
// ElementId is ignored. RoomKey and Area are the same but Location is different.
// So the comparer indicates that they are not "equal."
Assert.Equal(2, hashset.Count);
}
关于c# - C# 中的动态相等,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70839831/