c# - 有没有办法减少 Equals 和 GetHashCode 中的样板代码量?

标签 c# equals gethashcode

出于单元测试的目的,我经常需要覆盖 EqualsGetHashCode 方法。在此之后,我的类(class)开始看起来像这样:

public class TestItem
{
    public bool BoolValue { get; set; }

    public DateTime DateTimeValue { get; set; }

    public double DoubleValue { get; set; }

    public long LongValue { get; set; }

    public string StringValue { get; set; }

    public SomeEnumType EnumValue { get; set; }

    public decimal? NullableDecimal { get; set; }

    public override bool Equals(object obj)
    {
        var other = obj as TestItem;

        if (other == null)
        {
            return false;
        }

        if (object.ReferenceEquals(this, other))
        {
            return true;
        }

        return this.BoolValue == other.BoolValue
            && this.DateTimeValue == other.DateTimeValue
            && this.DoubleValue == other.DoubleValue // that's not a good way, but it's ok for demo
            && this.EnumValue == other.EnumValue
            && this.LongValue == other.LongValue
            && this.StringValue == other.StringValue
            && this.EnumValue == other.EnumValue
            && this.NullableDecimal == other.NullableDecimal;
    }

    public override int GetHashCode()
    {
        return this.BoolValue.GetHashCode()
            ^ this.DateTimeValue.GetHashCode()
            ^ this.DoubleValue.GetHashCode()
            ^ this.EnumValue.GetHashCode()
            ^ this.LongValue.GetHashCode()
            ^ this.NullableDecimal.GetHashCode()
            ^ (this.StringValue != null ? this.StringValue.GetHashCode() : 0);
    }
}

虽然这并不难,但一次又一次地在 EqualsGetHashCode 中维护相同字段的列表会变得乏味且容易出错。有什么方法可以只列出一次用于相等性检查和哈希码功能的文件吗? Equals 和 GetHashCode 应该按照这个设置列表来实现。

在我的想象中,这样的设置列表的配置和用法可能看起来像

public class TestItem
{
    // same properties as before

    private static readonly EqualityFieldsSetup Setup = new EqualityFieldsSetup<TestItem>()
        .Add(o => o.BoolValue)
        .Add(o => o.DateTimeValue)
        // ... and so on
        // or even .Add(o => o.SomeFunction())

    public override bool Equals(object obj)
    {
        return Setup.Equals(this, obj);
    }

    public override int GetHashCode()
    {
        return Setup.GetHashCode(this);
    }
}

有一种方法可以在 java 中自动实现 hashCodeequalsproject lombok例如。我想知道是否有任何东西可以减少可用于 C# 的样板代码。

最佳答案

我认为有可能在 C# 中实现与 Lombok 几乎相同的东西,但目前我并没有那么雄心勃勃。

我相信这就是您所追求的(与您描述的几乎完全一样)。此实现确实将所有值类型装箱到对象中,因此它不是最有效的实现,但它应该足以满足您的单元测试目的。

public class EqualityFieldsSetup<T>
    where T : class
{
    private List<Func<T, object>> _propertySelectors;

    public EqualityFieldsSetup()
    {
        _propertySelectors = new List<Func<T, object>>();
    }

    public EqualityFieldsSetup<T> Add(Func<T, object> propertySelector)
    {
        _propertySelectors.Add(propertySelector);
        return this;
    }

    public bool Equals(T objA, object other)
    {
        //If both are null, then they are equal 
        //    (a condition I think you missed)
        if (objA == null && other == null)
            return true;

        T objB = other as T;

        if (objB == null)
            return false;

        if (object.ReferenceEquals(objA, objB))
            return true;

        foreach (Func<T, object> propertySelector in _propertySelectors)
        {
            object objAProperty = propertySelector.Invoke(objA);
            object objBProperty = propertySelector.Invoke(objB);

            //If both are null, then they are equal
            //   move on to the next property
            if (objAProperty == null && objBProperty == null)
                continue;

            //Boxing requires the use of Equals() instead of '=='
            if (objAProperty == null && objBProperty != null ||
                !objAProperty.Equals(objBProperty))
                return false;
        }

        return true;
    }

    public int GetHashCode(T obj)
    {
        int hashCode = 0;

        foreach (Func<T, object> propertySelector in _propertySelectors)
        {
            object objProperty = propertySelector.Invoke(obj);

            if (objProperty != null)
                hashCode ^= objProperty.GetHashCode();
        }

        return hashCode;
    }
}

关于c# - 有没有办法减少 Equals 和 GetHashCode 中的样板代码量?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17558135/

相关文章:

java - 覆盖等于检查类类型或原始 int

c# - String.GetHashCode() 返回不同的值

c# - 我如何获得 System.Windows.Window

java - 为什么我似乎能够将两个彼此 equal() 的对象添加到 TreeSet

java - [Java]indexOf是否使用equals?

c# - 获取哈希码(对象): why in some cases it's designed with an argument?

c# - DateTime.Now 是否有自己的 GetHashCode 实现来提供唯一的哈希值?

c# - Entity Framework 投影到类比选择 EF POCO 对象更快,为什么?

c# - 在 Visual Studio C 中可以编译的文件大小是否有限制?

c# - 条件 Lambda 表达式?