Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/325.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在没有不可变字段的类中重写Object.GetHashCode()时返回什么?_C#_Class_Overriding_Mutable_Gethashcode - Fatal编程技术网

C# 在没有不可变字段的类中重写Object.GetHashCode()时返回什么?

C# 在没有不可变字段的类中重写Object.GetHashCode()时返回什么?,c#,class,overriding,mutable,gethashcode,C#,Class,Overriding,Mutable,Gethashcode,好吧,在你因为网上有数百个类似的问题而发疯之前,我可以向你保证,我刚刚花了几个小时阅读了所有这些问题,还没有找到我问题的答案 背景: 基本上,我的一个大型应用程序遇到了这样一种情况:列表框上的一些绑定s。SelectedItem属性将停止工作,或者在对当前所选项目进行编辑后程序将崩溃。我最初在这里问这个问题,但没有得到答案 直到本周,我才有时间解决这个问题,当时我有好几天的时间来解决这个问题。现在长话短说,我找到了问题的原因。这是因为我的数据类型类重写了Equals方法,因此也重写了GetHas

好吧,在你因为网上有数百个类似的问题而发疯之前,我可以向你保证,我刚刚花了几个小时阅读了所有这些问题,还没有找到我问题的答案

背景: 基本上,我的一个大型应用程序遇到了这样一种情况:
列表框上的一些
绑定
s。SelectedItem
属性将停止工作,或者在对当前所选项目进行编辑后程序将崩溃。我最初在这里问这个问题,但没有得到答案

直到本周,我才有时间解决这个问题,当时我有好几天的时间来解决这个问题。现在长话短说,我找到了问题的原因。这是因为我的数据类型类重写了
Equals
方法,因此也重写了
GetHashCode
方法

现在对于那些不知道这个问题的人,我发现您只能使用不可变的字段/属性实现
GetHashCode
方法。摘自Harvey Kwok对帖子的回答,解释如下:

问题是字典和哈希集集合正在使用GetHashCode将每个项放入一个bucket中。如果hashcode是基于一些可变字段计算的,并且在将对象放入HashSet或Dictionary后字段确实发生了更改,则无法再从HashSet或Dictionary中找到该对象

所以实际的问题是因为我在
GetHashCode
方法中使用了可变属性造成的。当用户在UI中更改这些属性值时,对象的关联哈希代码值将更改,然后在其集合中再也找不到项

问题: 因此,我的问题是,在没有不可变字段的类中实现
GetHashCode
方法时,处理这种情况的最佳方法是什么?对不起,让我说得更具体一些,因为这个问题以前已经问过了

帖子中的答案表明,在这些情况下,最好只返回一个常量值。。。一些建议返回值
1
,而另一些建议返回素数。就我个人而言,我看不出这些建议之间有什么区别,因为我本以为它们中的任何一个都只能使用一个桶

此外,Eric Lippert博客上的文章有一个标题为指南的部分:散列码的分布必须是“随机”的,这突出了使用导致未使用足够存储桶的算法的陷阱。他警告说,算法会减少使用的存储桶数量,并在存储桶变得非常大时导致性能问题。当然,返回常量属于这一类

我有一个想法,就是在我的所有数据类型类(仅在C#中,而不是数据库中)中添加一个额外的
Guid
字段,专门用于并且仅用于
GetHashCode
方法。因此,我想在这篇长篇介绍的最后,我的实际问题是哪种实现更好?总结如下:

总结: 在没有不可变字段的类中重写Object.GetHashCode()时,最好是从
GetHashCode
方法返回一个常量,还是为每个类创建一个额外的
readonly
字段,仅用于
GetHashCode
方法?如果我应该添加一个新字段,那么它应该是什么类型?我不应该在
Equals
方法中包含它吗


虽然我很高兴从任何人那里得到答案,但我真的希望从对这一主题有着丰富知识的高级开发人员那里得到答案。

如果类确实不包含任何可以计算哈希值的常量,那么我将使用比GUID更简单的东西。只需在类(或包装器类)中使用持久化的随机数。

一种简单的方法是将哈希代码存储在私有成员中,并在第一次使用时生成它。如果您的实体不经常更改,并且您不打算使用两个相等的不同对象(Equals方法返回true)作为字典中的键,那么这应该很好:

private int? _hashCode;

public override int GetHashCode() {
   if (!_hashCode.HasValue)
      _hashCode = Property1.GetHashCode() ^ Property2.GetHashCode() etc... based on whatever you use in your equals method
   return _hashCode.Value;
}
但是,如果您有对象a和对象b,其中a.Equals(b)=true,并且您在字典中使用a作为键(dictionary[a]=value)存储条目。
如果a没有改变,那么dictionary[b]将返回值,但是,如果在dictionary中存储条目后更改a,那么dictionary[b]很可能会失败。
唯一的解决方法是在任何键发生更改时重新刷新字典。

我要么创建一个额外的
只读
字段,要么抛出
NotSupportedException
。在我看来,另一种选择是毫无意义的。让我们看看原因

不同(固定)散列码 提供不同的散列码很容易,例如:

class Sample
{
    private static int counter;
    private readonly int hashCode;

    public Sample() { this.hashCode = counter++; }

    public override int GetHashCode()
    {
        return this.hashCode;
    }

    public override bool Equals(object other)
    {
        return object.ReferenceEquals(this, other);
    }
}
从技术上讲,你必须注意创建太多的对象,并在这里溢出
计数器,但在实践中,我认为这对任何人都不是问题

这种方法的问题是,实例之间永远不会比较相等。但是,如果您只想将
Sample
的实例用作其他类型集合的索引,那么这是非常好的

常数散列码 如果在任何情况下,不同的实例应该比较相等,那么乍一看,除了返回一个常量之外,您别无选择。但这会把你带到哪里去呢

在容器中定位实例将始终退化为等效于线性搜索。因此,实际上,通过返回一个常量,您允许用户为您的类创建一个键控容器,但该容器将显示
LinkedList
的性能特征。这对熟悉你们班的人来说可能是显而易见的,但就我个人而言,我认为这是错误的