簡體   English   中英

為IRevertibleChangeTracking類實現GetHashCode

[英]Implement GetHashCode for IRevertibleChangeTracking class

我當前的項目使用Dapper來基於數據庫中的行構建一個類。

我削減了班級以專注於問題的相等性組件,因此請告知是否需要其他代碼:

public class Computer
{
    public int Id { get; internal set; }
    public string OperatingSystem { get; set; }
}

public class Hardware : IRevertibleChangeTracking
{
    public int Id { get; internal set; }

    private string serialNumber;
    public string SerialNumber
    {
        get => this.serialNumber;
        set => this.SetField(ref this.serialNumber, value);
    }

    protected bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
    {
        // compare against OriginalValues and store changed value in Changes
    }

    public ObservableCollection<Computer> Computers { get; }

    private Dictionary<string, object> OriginalValues { get; set; }

    // Equals override
    public override bool Equals(object value)
    {
        return this.Equals(value as Hardware);
    }

    // Equals implementation
    public bool Equals(Hardware hardware)
    {
        if (ReferenceEquals(null, hardware)) return false;
        if (ReferenceEquals(this, hardware)) return true;

        return object.Equals(this.Id, hardware.Id)
           && string.Equals(this.SerialNumber, hardware.SerialNumber)
           && this.Computers.SequenceEquals(hardware.Computers);

    public static bool operator ==(Hardware hardwareA, Hardware hardwareB)
    {
        if (object.ReferenceEquals(hardwareA, hardwareB))
        {
            return true;
        }

        return !object.ReferenceEquals(null, hardwareA) && hardwareA.Equals(hardwareB);
    }       

    public static bool operator !=(Hardware hardwareA, Hardware hardwareB)
    {
        return !(hardwareA == hardwareB);
    }

    public override void AcceptChanges()
    {
        // clear changes and update original calues
    }

    public override void RejectChanges()
    {
        // clear changes and revert object state
    }
}

我重寫了Equals,以允許我的類將自己與其他實例進行比較,並簡化涉及所有屬性的單元測試:

[Test]
public void CanCheckTwoHardwareAreTheSameWithEquals()
{
    var firstHardware = Database.GetHardwareById(931);
    var secondHardware = Database.GetHardwareById(931);

    // i would rather this than asserting 50 properties are equal
    Assert.IsTrue(firstAsset == secondAsset);
}

然后,Overrideing Equals會在Visual Studio中生成一個警告,以同時覆蓋GetHashCode。 根據我自己的研究( MSDN ,Googling,類似的StackOverflow問題),我了解到:

  • 如果覆蓋等於,則還應該覆蓋GetHashCode
  • 如果一個對象等於另一個對象,則GetHashCode應該為兩個對象提供相同的值
  • 各種內部集合使用GetHashCode,並且在將我的類與這些集合一起使用時,未能正確覆蓋它會導致問題
  • GetHashCode中的值在對象的生存期內不應更改

由於我的類的屬性都可以更改(即使將ID設置為-1並為新對象更新),也可以不依賴當前值,而是使用ID屬性和OriginalValues字典來實現GetHashCode,因為它們的更改最少:

public override int GetHashCode()
{
    unchecked
    {
        // Choose large primes to avoid hashing collisions
        const int HashBase = (int)2166136261;
        const int HashMultiplier = 16777619;

        var hash = HashBase;
        hash = (hash * HashMultiplier) ^ (!object.ReferenceEquals(null, this.Id) ? this.Id.GetHashCode() : 0);
        hash = (hash * HashMultiplier) ^ (!object.ReferenceEquals(null, this.OriginalValues["SerialNumber"]) ? this.OriginalValues["SerialNumber"].GetHashCode() : 0);
        return hash;
    }
}

但是,這只有在有人在我的類上調用AcceptChanges()方法並且OriginalValues發生更改(從而為GetHashCode()生成不同的值AcceptChanges()才起作用。

我在這里還看到了一些答案,這些答案僅return 0作為GetHashCode()的實現。

public override int GetHashCode()
{
    return 0;
}

無論我的類的屬性被更改了多少次,這都將返回一個始終相同的值。

問題

  1. return 0是對屬性應更改的可變對象的GetHashCode的有效實現嗎?
  2. 此實現有什么缺點要注意嗎? (字典查找的性能較差嗎?)

如果只需要Equals進行測試,則不要修改您的課程。

我更喜歡使用FluentAssertions.BeEquivalentTo方法。 它按屬性比較對象。

firstAsset.Should().BeEquivalentTo(secondAsset);

因此,您在課堂上的代碼將保持干凈。

我最終將創建代碼從類移到了工廠類,因為工廠已經負責根據已存在的行創建類的實例。

這樣做意味着類的Id屬性在創建后就不會更改,這使我可以使用以下方法創建唯一的哈希碼:

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM