c# - C# 中的动态相等

标签 c# .net oop

假设我有以下类(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 ,我可以获取值和类型,但比较不起作用。

我更愿意将属性作为属性名称(字符串)列表传递。

我想使用 IEqualityComparerIEquitable (我将此对象用作 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/

相关文章:

c# - 尝试添加 silverlight 服务时出现 "cannot import wsdl:binding"错误

C# 编译器优化和 volatile 关键字

php - session 的乐趣

oop - 面向对象设计的建议

java - 泛型和 API 设计

c# - 框架中的 WPF 页面导航,如何从我导航到的页面访问我的原始框架?

c# - 使用 C# 进行电子邮件排队

c# - Xamarin Android 在 ListView 中单击按钮并获取项目位置

c# - Linq to SQL 内部 .NET Framework 数据提供程序错误 1025

c# - 使 .NET 可执行文件在第一时间加载得更快