![](/img/trans.png)
[英]Implement GetHashCode on a class that has wildcard Equatability
[英]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問題),我了解到:
由於我的類的屬性都可以更改(即使將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;
}
無論我的類的屬性被更改了多少次,這都將返回一個始終相同的值。
return 0
是對屬性應更改的可變對象的GetHashCode
的有效實現嗎? 如果只需要Equals
進行測試,則不要修改您的課程。
我更喜歡使用FluentAssertions.BeEquivalentTo
方法。 它按屬性比較對象。
firstAsset.Should().BeEquivalentTo(secondAsset);
因此,您在課堂上的代碼將保持干凈。
我最終將創建代碼從類移到了工廠類,因為工廠已經負責根據已存在的行創建類的實例。
這樣做意味着類的Id屬性在創建后就不會更改,這使我可以使用以下方法創建唯一的哈希碼:
public override int GetHashCode()
{
return this.Id.GetHashCode();
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.