在一个类中有一个GUID私有属性,以便在GetHashCode重写中使用它,可以吗?

时间:2021-10-03 16:11:32

Is it OK to have a GUID private property in a class in order to use it in GetHashCode override?

为了在GetHashCode覆盖中使用GUID私有属性,是否可以在类中使用它?

Something like:

喜欢的东西:

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
}

Yesterday I found out that in order to override GetHashCode we have to use only immutable members/fields of the class.

昨天我发现为了覆盖GetHashCode,我们必须只使用类的不可变成员/字段。

For many of my cases that is only the Id that is produced by identity of the Sql Server and for new instances that is 0.

对于我的许多情况,这只是Sql服务器标识生成的Id,对于新的实例,这是0。

So for many new objects (not persisted to database thus Id is 0) object hash code is the same. Correct?

因此,对于许多新对象(没有持久化到数据库中,因此Id为0),对象哈希代码是相同的。正确吗?

Would it be a solution to use GUID like the example above? Thanks.

这是一个使用GUID的解决方案吗?谢谢。

EDIT Class after comments

编辑类评论后

So after your comments I've changed it to:

在你的评论之后,我把它改成:

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
    }

I guess that this is OK since Voucher is immutable.

我想这没问题,因为凭证是不可变的。

But if members Number and Date were not immutable and could be accessed - altered outside the class? Then what is the solution? Is it enough just to document the class something like "Cannot be used in HashCode depended Lists"?

但是如果成员编号和日期不是不可变的,并且可以在类之外被访问?那么解决方案是什么?仅仅记录类,比如“无法在与HashCode相关的列表中使用”就足够了吗?

5 个解决方案

#1


2  

No, it is not okay to use a GUID in this way as it breaks what GetHashCode() is meant to do, which is calculate a hash of the contents of the object where if two objects have the same content, they will have the same hash.

不,不可以这样使用GUID,因为它破坏了GetHashCode()的目的,即计算对象内容的散列,如果两个对象具有相同的内容,那么它们将具有相同的散列。

You should rather implement GetHashCode() like in this question : SO - What is the best algorithm for GetHashCode? You should take the entire contents of the object into account for the hash.

您应该实现GetHashCode(),就像这个问题:那么——GetHashCode的最佳算法是什么?您应该将对象的全部内容考虑到散列。

The relevant code from the above link is:

以上链接的相关代码为:

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;
    }
}

#2


1  

Using a Guid is unnecessary as others have mentioned. But I think I understand the struggle in terms of comparing unpersisted objects. We use three levels when comparing objects:

使用Guid是不必要的,就像其他人提到的那样。但是我认为我理解比较非持久性对象的困难。我们在比较对象时使用三个层次:

AreSame() = represented by being the same space in memory. We don't really use a method here because 'x == y' does this nicely.

AreSame() =表示为内存中的相同空间。这里我们不用方法,因为x = y很好。

AreEqual() = equality, for us, is defined by having the same Id, including 0. If the id is default(int) then we refer to it as 'empty'. So much of the time we're testing for new objects with a method 'IsNullOrEmpty()' which nicely describes an object that either doesn't exist, or an object that is fresh and hasn't yet been persisted.

对我们来说,相等的定义是具有相同的Id,包括0。如果id是default(int),那么我们将其称为“empty”。很多时候,我们都在用“IsNullOrEmpty()”方法测试新对象,这个方法很好地描述了一个不存在的对象,或者是一个新对象,并且还没有被持久化。

//querying distinct persisted vouchers
var vouchers = vouchers.Where(w=>!w.IsNullOrEmpty()).Distinct();

AreEquivalent() - This is based on the individual properties of an object (e.g. a composite key) and is very subjective to the object. For instance, if your number/date represented a distinct voucher, then that would be used for equivalency. You can use an anonymous object or something here to keep it clear.

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));
    }

#3


0  

No.

不。

The contract specifies that two equal objects should have the same hashCode.

契约指定两个相等的对象应该具有相同的hashCode。

In your case, if you create two objects with the same contents, your hashcode will be different for both instances. This breaks the contract and potentially the behaviour of components that rely on hashCode.

在您的例子中,如果您创建了两个具有相同内容的对象,那么您的hashcode对于两个实例都是不同的。这就破坏了契约和依赖于hashCode的组件的行为。

Also, when you override GetHashCode(), you must also override Equals(object).

此外,当重写GetHashCode()时,还必须重写Equals(object)。

#4


0  

When overriding Equals and GetHashCode the rule that you should follow is that if two instances are equal then they should have the same hash code. You are violating that rule by creating unique hash codes for instances that are equal. This will cause issues with collections like Dictionary and HashSet that rely on GetHashCode returning the same value for equal items.

当重写Equals和GetHashCode时,您应该遵循的规则是,如果两个实例是相等的,那么它们应该具有相同的散列代码。通过为相等的实例创建惟一的哈希代码,您违反了这条规则。这将导致类似Dictionary和HashSet这样的集合出现问题,它们依赖于GetHashCode为相等的项返回相同的值。

#5


0  

Even though everyone else sais it's not OK, I'd go for 'Well, that depends'.

即使其他人都认为这是不对的,我还是会选择“嗯,那要看情况”。

One thing where GUID's are useful are for distributed systems. After all, they're globally unique identifiers - so if you are checking for equality across the boundaries of multiple processes / instances / persistency / etc and passing objects across those boundaries, I'd say, you're doing the right thing.

GUID在分布式系统中很有用。毕竟,它们是全局惟一的标识符——因此,如果您正在检查跨多个进程/实例/持久性/等的边界上的相等性,以及跨越这些边界的对象,我就会说,您正在做正确的事情。

I regularly use (sequential) GUID's as ID's for databases as well. While most DBA's don't like this for performance reasons, it has the benefit that you don't need to check before doing an insert, thereby saving a network roundtrip. Personally, I believe this is the future 'best practice' for database keys. Still, I acknowledge that that's very debatable, and currently it's not considered a good practice I suppose.

我也经常使用(顺序)GUID作为数据库的ID。虽然大多数DBA因为性能原因不喜欢这样做,但它的好处是,在进行插入之前不需要检查,从而节省了网络往返。我个人认为这是未来数据库键的“最佳实践”。尽管如此,我承认这是很有争议的,而且目前我认为这并不是一个好的实践。

That said, you're probably not after these kinds of things. :-)

也就是说,你可能不是在追求这些东西。:-)

If you just want to check for equality in the instance of your program, you should consider what you want to achieve. If you want to group instances by database ID (f.ex. to check for conflicts), you want to create equality using the key members (ID is probably sufficient in this case since it seems to be a database record on 1 database instance).

如果您只是想检查程序实例中的平等,那么您应该考虑您想要实现的目标。如果您想按数据库ID (f.ex)对实例进行分组。为了检查冲突),您希望使用键成员创建等式(ID在本例中可能已经足够了,因为它似乎是一个数据库实例上的数据库记录)。

If you want unique objects in your application, you can implement equality by yourself (note: the default implementation of Object already works like this). The way to do that is to use RuntimeHelpers.GetHashCode(this) and Object.ReferenceEquals(this, o);. This basically uses the pointers for comparison.

如果希望应用程序中有唯一的对象,可以自己实现等式(注意:对象的默认实现已经这样工作)。这样做的方法是使用runtimehelperson . gethashcode (this)和对象。ReferenceEquals(o);。这基本上是使用指针进行比较。

To summarize: What you're after depends on the implementation. Usually, you want equality because you're f.ex. filling a Dictionary or a HashSet. This also requires you override both Equals and GetHashCode. The implementation you should use is the one that makes most sense in that context.

总结:您所追求的内容取决于实现。通常,你想要平等,因为你是f.ex。填充字典或HashSet。这还需要重写Equals和GetHashCode。您应该使用的实现是在此上下文中最有意义的实现。

#1


2  

No, it is not okay to use a GUID in this way as it breaks what GetHashCode() is meant to do, which is calculate a hash of the contents of the object where if two objects have the same content, they will have the same hash.

不,不可以这样使用GUID,因为它破坏了GetHashCode()的目的,即计算对象内容的散列,如果两个对象具有相同的内容,那么它们将具有相同的散列。

You should rather implement GetHashCode() like in this question : SO - What is the best algorithm for GetHashCode? You should take the entire contents of the object into account for the hash.

您应该实现GetHashCode(),就像这个问题:那么——GetHashCode的最佳算法是什么?您应该将对象的全部内容考虑到散列。

The relevant code from the above link is:

以上链接的相关代码为:

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;
    }
}

#2


1  

Using a Guid is unnecessary as others have mentioned. But I think I understand the struggle in terms of comparing unpersisted objects. We use three levels when comparing objects:

使用Guid是不必要的,就像其他人提到的那样。但是我认为我理解比较非持久性对象的困难。我们在比较对象时使用三个层次:

AreSame() = represented by being the same space in memory. We don't really use a method here because 'x == y' does this nicely.

AreSame() =表示为内存中的相同空间。这里我们不用方法,因为x = y很好。

AreEqual() = equality, for us, is defined by having the same Id, including 0. If the id is default(int) then we refer to it as 'empty'. So much of the time we're testing for new objects with a method 'IsNullOrEmpty()' which nicely describes an object that either doesn't exist, or an object that is fresh and hasn't yet been persisted.

对我们来说,相等的定义是具有相同的Id,包括0。如果id是default(int),那么我们将其称为“empty”。很多时候,我们都在用“IsNullOrEmpty()”方法测试新对象,这个方法很好地描述了一个不存在的对象,或者是一个新对象,并且还没有被持久化。

//querying distinct persisted vouchers
var vouchers = vouchers.Where(w=>!w.IsNullOrEmpty()).Distinct();

AreEquivalent() - This is based on the individual properties of an object (e.g. a composite key) and is very subjective to the object. For instance, if your number/date represented a distinct voucher, then that would be used for equivalency. You can use an anonymous object or something here to keep it clear.

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));
    }

#3


0  

No.

不。

The contract specifies that two equal objects should have the same hashCode.

契约指定两个相等的对象应该具有相同的hashCode。

In your case, if you create two objects with the same contents, your hashcode will be different for both instances. This breaks the contract and potentially the behaviour of components that rely on hashCode.

在您的例子中,如果您创建了两个具有相同内容的对象,那么您的hashcode对于两个实例都是不同的。这就破坏了契约和依赖于hashCode的组件的行为。

Also, when you override GetHashCode(), you must also override Equals(object).

此外,当重写GetHashCode()时,还必须重写Equals(object)。

#4


0  

When overriding Equals and GetHashCode the rule that you should follow is that if two instances are equal then they should have the same hash code. You are violating that rule by creating unique hash codes for instances that are equal. This will cause issues with collections like Dictionary and HashSet that rely on GetHashCode returning the same value for equal items.

当重写Equals和GetHashCode时,您应该遵循的规则是,如果两个实例是相等的,那么它们应该具有相同的散列代码。通过为相等的实例创建惟一的哈希代码,您违反了这条规则。这将导致类似Dictionary和HashSet这样的集合出现问题,它们依赖于GetHashCode为相等的项返回相同的值。

#5


0  

Even though everyone else sais it's not OK, I'd go for 'Well, that depends'.

即使其他人都认为这是不对的,我还是会选择“嗯,那要看情况”。

One thing where GUID's are useful are for distributed systems. After all, they're globally unique identifiers - so if you are checking for equality across the boundaries of multiple processes / instances / persistency / etc and passing objects across those boundaries, I'd say, you're doing the right thing.

GUID在分布式系统中很有用。毕竟,它们是全局惟一的标识符——因此,如果您正在检查跨多个进程/实例/持久性/等的边界上的相等性,以及跨越这些边界的对象,我就会说,您正在做正确的事情。

I regularly use (sequential) GUID's as ID's for databases as well. While most DBA's don't like this for performance reasons, it has the benefit that you don't need to check before doing an insert, thereby saving a network roundtrip. Personally, I believe this is the future 'best practice' for database keys. Still, I acknowledge that that's very debatable, and currently it's not considered a good practice I suppose.

我也经常使用(顺序)GUID作为数据库的ID。虽然大多数DBA因为性能原因不喜欢这样做,但它的好处是,在进行插入之前不需要检查,从而节省了网络往返。我个人认为这是未来数据库键的“最佳实践”。尽管如此,我承认这是很有争议的,而且目前我认为这并不是一个好的实践。

That said, you're probably not after these kinds of things. :-)

也就是说,你可能不是在追求这些东西。:-)

If you just want to check for equality in the instance of your program, you should consider what you want to achieve. If you want to group instances by database ID (f.ex. to check for conflicts), you want to create equality using the key members (ID is probably sufficient in this case since it seems to be a database record on 1 database instance).

如果您只是想检查程序实例中的平等,那么您应该考虑您想要实现的目标。如果您想按数据库ID (f.ex)对实例进行分组。为了检查冲突),您希望使用键成员创建等式(ID在本例中可能已经足够了,因为它似乎是一个数据库实例上的数据库记录)。

If you want unique objects in your application, you can implement equality by yourself (note: the default implementation of Object already works like this). The way to do that is to use RuntimeHelpers.GetHashCode(this) and Object.ReferenceEquals(this, o);. This basically uses the pointers for comparison.

如果希望应用程序中有唯一的对象,可以自己实现等式(注意:对象的默认实现已经这样工作)。这样做的方法是使用runtimehelperson . gethashcode (this)和对象。ReferenceEquals(o);。这基本上是使用指针进行比较。

To summarize: What you're after depends on the implementation. Usually, you want equality because you're f.ex. filling a Dictionary or a HashSet. This also requires you override both Equals and GetHashCode. The implementation you should use is the one that makes most sense in that context.

总结:您所追求的内容取决于实现。通常,你想要平等,因为你是f.ex。填充字典或HashSet。这还需要重写Equals和GetHashCode。您应该使用的实现是在此上下文中最有意义的实现。