首页 文章

可以在类中使用GUID私有属性,以便在GetHashCode覆盖中使用它吗?

提问于
浏览
5

可以在类中使用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,我们只能使用该类的不可变成员/字段 .

对于我的许多情况,只有通过Sql Server的标识生成的Id和0的新实例 .

因此,对于许多新对象(未持久化到数据库,因此Id为0),对象哈希码是相同的 . 正确?

它会像上面的例子一样使用GUID吗?谢谢 .

EDIT 评论后的课程

所以在您发表评论后我将其更改为:

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
    }

我想这是可以的,因为凭证是不可变的 .

但是,如果成员编号和日期不是一成不变的,可以访问 - 在课堂外改变?那么解决方案是什么?仅仅记录类“不能在HashCode依赖列表中使用”这样的内容是否足够?

5 回答

  • 2

    不,以这种方式使用 GUID 是不行的,因为它打破了 GetHashCode() 的意图,即计算对象内容的散列,如果两个对象具有相同的内容,它们将具有相同的散列 .

    你应该在这个问题中实现 GetHashCode()SO - What is the best algorithm for 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;
        }
    }
    
  • 0

    正如其他人所提到的,使用Guid是不必要的 . 但我认为我理解在比较未经加工的物体方面的斗争 . 比较对象时我们使用三个级别:

    AreSame() =由内存中的相同空间表示 . 我们没有't really use a method here because ' x == y'做得很好 .

    AreEqual() =对于我们来说,相等是通过具有相同的Id来定义的,包括0.如果id是default(int),那么我们将其称为'empty' . 我们大部分时间都存在,或者是一个新鲜但尚未持久存在的对象.'re testing for new objects with a method ' IsNullOrEmpty()' which nicely describes an object that either doesn' t存在 .

    //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));
        }
    
  • 0

    没有 .

    Contract 规定两个相等的对象应具有相同的hashCode .

    在您的情况下,如果您创建具有相同内容的两个对象,则两个实例的哈希码将不同 . 这打破了 Contract 以及可能依赖于hashCode的组件的行为 .

    此外,当您覆盖 GetHashCode() 时,您还必须覆盖 Equals(object) .

  • 0

    当覆盖 EqualsGetHashCode 时,您应遵循的规则是,如果两个实例相等,则它们应具有相同的哈希码 . 您通过为相同的实例创建唯一的哈希码来违反该规则 . 这将导致像 DictionaryHashSet 这样的集合出现问题,这些集合依赖 GetHashCode 为相等的项目返回相同的值 .

  • 1

    即使其他所有人都不满意,我也会选择'嗯,这取决于' .

    GUID有用的一件事是分布式系统 . 毕竟,它们是全局唯一标识符 - 所以如果你要检查跨多个进程/实例/持久性/等的边界是否相等,并且跨越这些边界传递对象,我会说,你做的是正确的 .

    我经常使用(顺序)GUID作为数据库的ID . 虽然大多数DBA由于性能原因不喜欢这种情况,但它的好处是您在插入之前无需检查,从而节省了网络往返 . 就个人而言,我相信这是数据库密钥未来的“最佳实践” . 尽管如此,我承认这是非常有争议的,目前我认为这不是一个好习惯 .

    也就是说,你可能不会追求这些事情 . :-)

    如果您只想在程序实例中检查是否相等,则应考虑要实现的目标 . 如果要按数据库ID(f.ex.检查冲突)对实例进行分组,则需要使用密钥成员创建相等(在这种情况下ID可能已足够,因为它似乎是1个数据库实例上的数据库记录) .

    如果你想在你的应用程序中使用唯一对象,你可以自己实现相等(注意: Object 的默认实现已经像这样工作) . 这样做的方法是使用 RuntimeHelpers.GetHashCode(this)Object.ReferenceEquals(this, o); . 这基本上使用指针进行比较 .

    总结一下:你're after depends on the implementation. Usually, you want equality because you' re f.ex.填写 DictionaryHashSet . 这还要求您覆盖 EqualsGetHashCode . 您应该使用的实现是在该上下文中最有意义的实现 .

相关问题