为 IRevertibleChangeTracking 实施 GetHashCode class
Implement GetHashCode for IRevertibleChangeTracking class
我当前的项目使用 Dapper 基于数据库中的行构建 class。
我已经缩减了我的 class 以专注于我问题的平等部分,所以如果需要任何额外的代码,请告诉我:
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 以允许我的 class 将自身与其他实例进行比较并简化涉及所有属性的单元测试:
[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);
}
Overriding Equals 然后在 Visual Studio 中生成了一个警告也覆盖了 GetHashCode。根据我自己的研究(MSDN、谷歌搜索、类似的 Whosebug 问题)我了解到:
- 如果您覆盖 Equals,您也应该覆盖 GetHashCode
- 如果一个对象等于另一个对象,GetHashCode 应该为两个对象提供相同的值
- GetHashCode 被各种内部集合使用,未能正确覆盖它可能会导致在将我的 class 与这些集合一起使用时出现问题
- GetHashCode 的值在对象的生命周期内不应更改
由于我的 class 的属性都可以更改(甚至 ID 设置为 -1 并为新对象更新),而不是依赖于当前值,我可以使用 ID 属性 和 OriginalValues 字典,因为它们变化最少:
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;
}
}
然而,只有当有人在我的 class 上调用 AcceptChanges()
方法并且 OriginalValues 被更改(从而为 GetHashCode()
产生不同的值)时,这才会起作用。
我在这里也看到了一些答案,它们只是 return 0
作为 GetHashCode()
的实现
public override int GetHashCode()
{
return 0;
}
这将 return 一个始终相同的值,无论我 class 上的属性更改多少次。
问题
return 0
是否是 GetHashCode
的有效实现,适用于属性预计会更改的可变对象?
- 此实施是否有任何需要注意的缺点? (字典查找性能下降?)
如果您只需要 Equals
进行测试,请不要修改您的 class。
我更喜欢使用FluentAssertions.BeEquivalentTo
方法。它按属性比较对象。
firstAsset.Should().BeEquivalentTo(secondAsset);
所以您在 class 中的代码将保持干净。
我最终将创建代码从我的 class 移到了工厂 class,因为工厂已经负责根据已经存在的行创建我的 class 的实例。
这样做意味着我的 class 上的 ID 属性 在创建后永远不会改变,这使我可以使用
创建一个唯一的哈希码
public override int GetHashCode()
{
return this.Id.GetHashCode();
}
我当前的项目使用 Dapper 基于数据库中的行构建 class。
我已经缩减了我的 class 以专注于我问题的平等部分,所以如果需要任何额外的代码,请告诉我:
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 以允许我的 class 将自身与其他实例进行比较并简化涉及所有属性的单元测试:
[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);
}
Overriding Equals 然后在 Visual Studio 中生成了一个警告也覆盖了 GetHashCode。根据我自己的研究(MSDN、谷歌搜索、类似的 Whosebug 问题)我了解到:
- 如果您覆盖 Equals,您也应该覆盖 GetHashCode
- 如果一个对象等于另一个对象,GetHashCode 应该为两个对象提供相同的值
- GetHashCode 被各种内部集合使用,未能正确覆盖它可能会导致在将我的 class 与这些集合一起使用时出现问题
- GetHashCode 的值在对象的生命周期内不应更改
由于我的 class 的属性都可以更改(甚至 ID 设置为 -1 并为新对象更新),而不是依赖于当前值,我可以使用 ID 属性 和 OriginalValues 字典,因为它们变化最少:
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;
}
}
然而,只有当有人在我的 class 上调用 AcceptChanges()
方法并且 OriginalValues 被更改(从而为 GetHashCode()
产生不同的值)时,这才会起作用。
我在这里也看到了一些答案,它们只是 return 0
作为 GetHashCode()
public override int GetHashCode()
{
return 0;
}
这将 return 一个始终相同的值,无论我 class 上的属性更改多少次。
问题
return 0
是否是GetHashCode
的有效实现,适用于属性预计会更改的可变对象?- 此实施是否有任何需要注意的缺点? (字典查找性能下降?)
如果您只需要 Equals
进行测试,请不要修改您的 class。
我更喜欢使用FluentAssertions.BeEquivalentTo
方法。它按属性比较对象。
firstAsset.Should().BeEquivalentTo(secondAsset);
所以您在 class 中的代码将保持干净。
我最终将创建代码从我的 class 移到了工厂 class,因为工厂已经负责根据已经存在的行创建我的 class 的实例。
这样做意味着我的 class 上的 ID 属性 在创建后永远不会改变,这使我可以使用
创建一个唯一的哈希码public override int GetHashCode()
{
return this.Id.GetHashCode();
}