我当前的项目使用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,Google,类似的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()
产生不同的值)后才有效。
我在这里也看到了一些答案,它们只是将return 0
作为GetHashCode()
的实现
public override int GetHashCode()
{
return 0;
}
无论我的类的属性被更改了多少次,返回的值始终是相同的。
return 0
是GetHashCode
的有效实现,用于预期其属性会发生变化的可变对象吗?答案 0 :(得分:0)
如果仅需要Equals
进行测试,则不要修改您的课程。
我更喜欢使用FluentAssertions.BeEquivalentTo
方法。它按属性比较对象。
firstAsset.Should().BeEquivalentTo(secondAsset);
让您在课堂上的代码保持整洁。
答案 1 :(得分:0)
我最终将创建代码从我的类移到了工厂类,因为工厂已经负责根据已经存在的行创建我的类的实例。
这样做意味着类的ID属性在创建后就不会更改,这使我可以使用以下方式创建唯一的哈希码
public override int GetHashCode()
{
return this.Id.GetHashCode();
}