在类中具有GUID私有属性以便在GetHashCode重写中使用它可以吗
本文关键字:重写 GUID 属性 GetHashCode | 更新日期: 2023-09-27 17:54:38
在类中具有GUID私有属性以便在GetHashCode重写中使用它可以吗?
类似于:
public class Voucher : IComparable<Voucher>, IComparable, IEquatable<Voucher>
{
private Guid? _guid;
private Guid Guid
{
get
{
return _guid ?? (_guid = Guid.NewGuid()).GetValueOrDefault();
}
}
public int Id { get; private set; }
public string Number { get; private set; }
public DateTime Date { get; private set; }
public Voucher(string number, DateTime date)
{
Number = number;
Date = date;
}
public Voucher(int id, string number, DateTime date)
: this(number, date)
{
Id = id;
}
public override bool Equals(object obj)
{
return Equals(obj as Voucher);
}
public override int GetHashCode()
{
return Guid.GetHashCode();
}
public override string ToString()
{
return String.Format("[{0}] - [{1:dd/MM/yyyy}]", Number, Date);
}
#region IComparable<Voucher> Members
public int CompareTo(Voucher other)
{
if (other == null)
return -1;
if (Date != other.Date)
return Date.CompareTo(other.Date);
else
return Number.CompareTo(other.Number);
}
#endregion
#region IComparable Members
public int CompareTo(object obj)
{
return CompareTo(obj as Voucher);
}
#endregion
#region IEquatable<Voucher> Members
public bool Equals(Voucher other)
{
if (other != null)
return (Number == other.Number) && (Date == other.Date);
return false;
}
#endregion
}
昨天我发现,为了覆盖GetHashCode,我们必须只使用类的不可变成员/字段。
在我的许多情况下,这只是由SqlServer标识生成的Id,对于新实例,这是0。
因此,对于许多新对象(未持久化到数据库,因此Id为0(,对象哈希代码是相同的。对的
像上面的例子一样使用GUID是一个解决方案吗?谢谢
编辑评论后分类
所以在你的评论之后,我把它改为:
public class Voucher : IComparable<Voucher>, IComparable, IEquatable<Voucher>
{
public int Id { get; private set; }
public string Number { get; private set; }
public DateTime Date { get; private set; }
public Voucher(string number, DateTime date)
{
Number = number;
Date = date;
}
public Voucher(int id, string number, DateTime date)
: this(number, date)
{
Id = id;
}
public override bool Equals(object obj)
{
return Equals(obj as Voucher);
}
public override int GetHashCode()
{
return Number.GetHashCode() ^ Date.GetHashCode();
}
public override string ToString()
{
return String.Format("[{0}] - [{1:dd/MM/yyyy}]", Number, Date);
}
#region IComparable<Voucher> Members
public int CompareTo(Voucher other)
{
if (other == null)
return -1;
if (Date != other.Date)
return Date.CompareTo(other.Date);
else
return Number.CompareTo(other.Number);
}
#endregion
#region IComparable Members
public int CompareTo(object obj)
{
return CompareTo(obj as Voucher);
}
#endregion
#region IEquatable<Voucher> Members
public bool Equals(Voucher other)
{
if (other != null)
return (Number == other.Number) && (Date == other.Date);
return false;
}
#endregion
}
我想这是可以的,因为凭单是不可变的。
但是,如果成员Number和Date不是不可变的,并且可以在类外访问-更改?那么解决方案是什么呢?仅仅记录类就足够了吗?比如"不能在依赖哈希代码的列表中使用"?
不,以这种方式使用GUID
是不好的,因为它破坏了GetHashCode()
的功能,即计算对象内容的哈希,如果两个对象具有相同的内容,它们将具有相同的哈希。
您应该像下面这个问题一样实现GetHashCode()
:SO-GetHashCode的最佳算法是什么?您应该在散列中考虑对象的全部内容。
来自上述链接的相关代码为:
public override int GetHashCode()
{
unchecked // Overflow is fine, just wrap
{
int hash = 17;
// Suitable nullity checks etc, of course :)
hash = hash * 23 + field1.GetHashCode();
hash = hash * 23 + field2.GetHashCode();
hash = hash * 23 + field3.GetHashCode();
return hash;
}
}
正如其他人所提到的,使用Guid是不必要的。但我想我理解在比较未受阻碍的物体方面的斗争。我们在比较对象时使用三个级别:
AreSame((=表示为内存中的同一空间。我们在这里并没有真正使用方法,因为'x==y'做得很好。
AreEqual((=对我们来说,相等是通过具有相同的Id来定义的,包括0。如果id是default(int(,那么我们将其称为"empty"。很多时候,我们使用方法"IsNullOrEmpty(("测试新对象,该方法很好地描述了一个不存在的对象,或者一个新的、尚未持久化的对象。
//querying distinct persisted vouchers
var vouchers = vouchers.Where(w=>!w.IsNullOrEmpty()).Distinct();
AreEquivalent((-这是基于对象的各个属性(例如复合键(,对对象来说非常主观。例如,如果您的号码/日期代表一张不同的代金券,那么它将用于对等。你可以在这里使用匿名对象或其他东西来保持它的清晰。
//(warning: handle nulls appropriately, ideally by creating a better equalitycomparer here.).
public override bool AreEquivalent(Voucher voucher){
var propsAsAnonymous = v=>new{v.Number,v.Date};
return propsAsAnonymous(this).Equals(propsAsAnonymous(voucher));
}
编号。
约定指定两个相等的对象应该具有相同的hashCode。
在您的情况下,如果您创建了两个内容相同的对象,那么两个实例的哈希代码将不同。这破坏了契约,并可能破坏依赖hashCode的组件的行为。
此外,当覆盖GetHashCode()
时,还必须覆盖Equals(object)
。
重写Equals
和GetHashCode
时,应遵循的规则是,如果两个实例相等,则它们应具有相同的哈希代码。您为相等的实例创建了唯一的哈希代码,这违反了该规则。这将导致像Dictionary
和HashSet
这样的集合出现问题,它们依赖于GetHashCode
为相等的项目返回相同的值。
即使其他人都说这不好,我还是会选择"好吧,这取决于"。
GUID对分布式系统很有用。毕竟,它们是全局唯一的标识符,所以如果你在多个进程/实例/持久性等的边界上检查相等性,并在这些边界上传递对象,我想说,你做的是正确的。
我也经常使用(顺序的(GUID作为数据库的ID。虽然大多数DBA出于性能原因不喜欢这样做,但它的好处是在插入之前不需要检查,从而节省了网络往返时间。就我个人而言,我相信这是未来数据库密钥的"最佳实践"。尽管如此,我承认这是非常有争议的,而且目前我认为这不是一个好的做法。
也就是说,你可能不想做这些事情
如果你只是想在你的程序实例中检查平等性,你应该考虑你想要实现什么。如果要按数据库ID对实例进行分组(例如,检查冲突(,则需要使用键成员创建相等(在这种情况下,ID可能足够了,因为它似乎是1个数据库实例上的数据库记录(。
如果您在应用程序中想要唯一的对象,您可以自己实现相等(注意:Object
的默认实现已经这样工作了(。方法是使用RuntimeHelpers.GetHashCode(this)
和Object.ReferenceEquals(this, o);
。这基本上使用指针进行比较。
总结一下:你追求的是什么取决于实现。通常,你想要相等,因为你在填充一个Dictionary
或HashSet
。这还需要覆盖Equals
和GetHashCode
。您应该使用的实现是在该上下文中最有意义的实现。