本文由机器翻译。若要查看英语原文,请勾选“英语”复选框。 也可将鼠标指针移到文本上,在弹出窗口中显示英语原文。
翻译
英语

Object.GetHashCode 方法 ()

 

作为默认哈希函数。

命名空间:   System
程序集:  mscorlib(位于 mscorlib.dll)

public virtual int GetHashCode()

返回值

Type: System.Int32

当前对象的哈希代码。

哈希代码是一个数字值,用于插入和标识基于哈希的集合中的对象,例如Dictionary<TKey, TValue>类,Hashtable类或派生自类型DictionaryBase类。 GetHashCode方法为需要的对象相等性的快速检查的算法提供此哈希代码。

System_CAPS_note说明

有关如何在哈希表中使用哈希代码的信息以及一些其他的哈希代码算法,请参阅Hash Function Wikipedia 中的条目。

是相等的相等的返回哈希代码的两个对象。 但是,反过来并不成立︰ 相等的哈希代码并不表示对象相等性,因为不同 (不相等) 的对象可以具有相同的哈希代码。 此外,.NET Framework 并不保证的默认实现GetHashCode方法和此方法所返回的.NET Framework 版本和平台,例如,32 位和 64 位平台之间可能不同的值。 出于这些原因,请不用作此方法的默认实现的唯一对象标识符哈希的目的。 从此请按照以下两种结果操作︰

  • 不应假定相等的哈希代码表示对象是否相等。

  • 永远不应将存在,或者在其中创建它,在应用程序域使用的哈希代码,因为相同的对象可能哈希处理跨应用程序域、 过程和平台。

System_CAPS_warning警告

哈希代码旨在高效插入和基于哈希表的集合中查找。 哈希代码不是永久的值。 出于此原因︰

  • 不序列化哈希代码值或将它们存储在数据库中。

  • 不使用的哈希代码作为键从是键控集合中检索对象。

  • 不要跨应用程序域或进程发送的哈希代码。 在某些情况下,可能会基于每个进程或每个应用程序域计算哈希代码。

  • 不要使用而不是返回的加密哈希函数,如果你需要加密型强哈希值的哈希代码。 对于加密哈希,使用派生自的类System.Security.Cryptography.HashAlgorithmSystem.Security.Cryptography.KeyedHashAlgorithm类。

  • 先不要测试相等的哈希代码,以确定两个对象是否相等。 (不相等的对象可以具有相同的哈希代码。) 若要测试相等性,调用ReferenceEqualsEquals方法。

GetHashCode派生类型可以重写方法。 如果GetHashCode是不重写,哈希代码为引用类型计算通过调用Object.GetHashCode的基类,计算哈希代码的方法基于对象的引用; 有关详细信息,请参阅RuntimeHelpers.GetHashCode 换而言之,两个对象为其ReferenceEquals方法返回true具有相同的哈希代码。 如果值类型不会重写GetHashCodeValueType.GetHashCode基本类的方法使用反射来计算基于该类型的字段的值的哈希代码。 换而言之,其字段具有相等的值的值类型具有相等的哈希代码。 有关重写GetHashCode,请参阅"对继承者的说明"部分。

System_CAPS_warning警告

如果你重写GetHashCode方法,则应重写Equals,反之亦然。 如果你重写Equals方法返回true两个对象是否相等,重写的测试时GetHashCode方法必须返回两个对象相同的值。

如果用作哈希表中的键的对象未提供的有用实现GetHashCode,你可以指定哈希代码提供程序通过提供IEqualityComparer实现的重载之一Hashtable类构造函数。

当调用GetHashCode中类方法Windows 运行时,它不会覆盖的类提供的默认行为GetHashCode 这是由 .NET Framework 为 Windows 运行时 提供的支持的一部分 (请参见.NET Framework 对 Windows 应用商店应用程序和 Windows 运行时的支持情况)。 中的类Windows 运行时不继承Object,并且不要实施GetHashCode 但是,它们会显示为具有ToStringEquals(Object),和GetHashCode方法在 C# 或 Visual Basic 代码中,使用它们以及.NET Framework 为这些方法提供的默认行为时。

System_CAPS_note说明

Windows 运行时用 C# 或 Visual Basic 编写的类可以重写GetHashCode方法。

继承函数说明:

哈希函数用于快速将生成一个对象的值相对应的数字 (哈希代码)。 哈希函数通常是特定于每种类型,和的唯一性,必须使用至少一个实例字段作为输入。 使用静态字段的值,不应计算哈希代码。

类派生自ObjectGetHashCode方法才可以委托给基类Object.GetHashCode()仅当派生的类定义的相等性可引用相等性的实现。 默认实现GetHashCode对于引用类型返回的哈希代码,它等效于由一个RuntimeHelpers.GetHashCode(Object)方法。 您可以重写GetHashCode对于不可变的引用类型。 一般情况下,对于可变引用类型,则应重写GetHashCode才︰

  • 您可以计算字段不是可变; 中的哈希代码或

  • 你可以确保可变对象的哈希代码不会更改时的对象包含在一个集合,其中依赖于其哈希代码。

否则,你可能认为,在哈希表中丢失的可变对象。 如果你选择重写GetHashCode对于可变的引用类型,你的文档应将其清除该对象存储在哈希表中时,你的类型的用户不应修改对象值。

对于值类型,ValueType.GetHashCode提供一个默认哈希代码实现,使用反射。 你应考虑重写它以提高性能。

System_CAPS_note说明

有关详细信息和计算各种不同的方式的哈希代码的示例,请参阅示例部分。

哈希函数必须具有以下属性︰

  • 如果两个对象的比较结果为相等,GetHashCode每个对象的方法必须返回相同的值。 但是,如果两个对象不相等,比较GetHashCode两个对象的方法不需要返回不同的值。

  • GetHashCode对象的方法必须始终返回相同的哈希代码,只要不没有对确定返回值的对象的对象状态的任何修改Equals方法。 请注意,这是仅适用当前执行应用程序,并且,如果再次运行应用程序,也可以返回不同的哈希代码。

  • 为了获得最佳性能,哈希函数应生成均匀分布于所有输入,包括已大量群集化的输入。 含意为对对象状态的小修改应导致大型修改哈希表的最佳性能的生成哈希代码。

  • 哈希函数应该是成本较低,来计算。

  • GetHashCode方法不应引发异常。

例如,实现GetHashCode方法提供的String类返回相同的字符串值的相同的哈希代码。 因此,两个String对象返回相同的哈希代码,如果它们表示相同的字符串值。 此外,该方法使用的所有字符在字符串中来生成相当随机的分布式输出,即使输入群集在某些范围内 (例如,许多用户可能具有字符串包含仅的较低 128 个 ASCII 字符,即使字符串可以包含任何 65535 个 Unicode 字符也是如此)。

在类上提供的很好的哈希函数可能会将这些对象添加到哈希表的性能显著影响。 在具有键提供哈希函数的良好实现哈希表中,搜索的元素所用常量时间内的 (例如,o (1) 操作)。 哈希函数的不佳实现哈希表,在搜索的性能取决于哈希表中的项的数目 (例如,O (n) 操作,其中n是哈希表中的项的数目)。 恶意用户可以输入增加的冲突,会大大降低取决于哈希表,在以下情况下的应用程序的性能数量的数据︰

  • 当哈希函数生成频繁冲突。

  • 当哈希表中的对象的大部分生成相等或在大约等于另一个的哈希代码。

  • 当用户输入的数据从其计算的哈希代码。

派生类重写GetHashCode还必须重写Equals若要确保视为相等的两个对象具有相同的哈希代码; 否则为Hashtable类型可能无法正常工作。

计算具有相同或比范围较小的数字值的哈希代码的最简单方法之一Int32类型是只需返回该值。 下面的示例演示此类的实现Number结构。

using System;

public struct Number
{
   private int n;

   public Number(int value)
   {
      n = value;
   }

   public int Value
   {
      get { return n; }
   }

   public override bool Equals(Object obj)
   {
      if (obj == null || ! (obj is Number)) 
         return false;
      else
         return n == ((Number) obj).n;
   }      

   public override int GetHashCode()
   {
      return n;
   }

   public override string ToString()
   {
      return n.ToString();
   }
}

public class Example
{
   public static void Main()
   {
      Random rnd = new Random();
      for (int ctr = 0; ctr <= 9; ctr++) {
         int randomN = rnd.Next(Int32.MinValue, Int32.MaxValue);
         Number n = new Number(randomN);
         Console.WriteLine("n = {0,12}, hash code = {1,12}", n, n.GetHashCode());
      }   
   }
}
// The example displays output like the following:
//       n =   -634398368, hash code =   -634398368
//       n =   2136747730, hash code =   2136747730
//       n =  -1973417279, hash code =  -1973417279
//       n =   1101478715, hash code =   1101478715
//       n =   2078057429, hash code =   2078057429
//       n =   -334489950, hash code =   -334489950
//       n =    -68958230, hash code =    -68958230
//       n =   -379951485, hash code =   -379951485
//       n =    -31553685, hash code =    -31553685
//       n =   2105429592, hash code =   2105429592

通常情况下,类型具有多个可以参与生成的哈希代码的数据字段。 生成的哈希代码的一种方法是组合使用这些字段XOR (eXclusive OR)操作,如下面的示例中所示。

using System;

// A type that represents a 2-D point.
public struct Point
{
    private int x;
    private int y;

    public Point(int x, int y)
    {
       this.x = x;
       this.y = y;
    }

    public override bool Equals(Object obj)
    {
       if (! (obj is Point)) return false;

       Point p = (Point) obj;
       return x == p.x & y == p.y;
    }

    public override int GetHashCode()
    { 
        return x ^ y;
    } 
} 

public class Example
{
   public static void Main()
   {
      Point pt = new Point(5, 8);
      Console.WriteLine(pt.GetHashCode());

      pt = new Point(8, 5);
      Console.WriteLine(pt.GetHashCode());
   }
}
// The example displays the following output:
//       13
//       13

前面的示例返回 (n1,n2) 的同一哈希代码和 (n2,n1),因此,可能会生成不是所需的多个冲突。 有多种解决方案都是可用的以便在这些情况下的哈希代码不相同。 一个是要返回的哈希代码Tuple反映每个字段的顺序的对象。 下面的示例演示使用的可能实现Tuple<T1, T2>类。 但请注意,该实例化的性能开销Tuple对象可能会显著影响哈希表中存储大量对象的应用程序的整体性能。

using System;

public struct Point
{
    private int x;
    private int y;

    public Point(int x, int y)
    {
       this.x = x;
       this.y = y;
    }

    public override bool Equals(Object obj)
    {
       if (!(obj is Point)) return false;

       Point p = (Point) obj;
       return x == p.x & y == p.y;
    }

    public override int GetHashCode()
    { 
        return Tuple.Create(x, y).GetHashCode();
    } 
} 

public class Example
{
   public static void Main()
   {
        Point pt = new Point(5, 8);
        Console.WriteLine(pt.GetHashCode());

        pt = new Point(8, 5);
        Console.WriteLine(pt.GetHashCode());
   }
}
// The example displays the following output:
//       173
//       269

第二个备选解决方案涉及的两个或多个位由连续的字段的哈希代码进行左移权重的单个哈希代码。 最佳状况运行,而不是被丢弃,移出第 31 位的位应环绕而不是被放弃。 由于 C# 和 Visual Basic 中的左移运算符的情况下,位将被丢弃,这就需要创建一个左的 shift 包装方法如下所示︰

public int ShiftAndWrap(int value, int positions)
{
    positions = positions & 0x1F;

    // Save the existing bit pattern, but interpret it as an unsigned integer.
    uint number = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
    // Preserve the bits to be discarded.
    uint wrapped = number >> (32 - positions);
    // Shift and wrap the discarded bits.
    return BitConverter.ToInt32(BitConverter.GetBytes((number << positions) | wrapped), 0);
}

下面的示例然后使用此 shift 包装方法来计算的哈希代码Point前面的示例中使用结构。

using System;

public struct Point
{
    private int x;
    private int y;

    public Point(int x, int y)
    {
       this.x = x;
       this.y = y;
    }

    public override bool Equals(Object obj)
    {
       if (!(obj is Point)) return false;

       Point p = (Point) obj;
       return x == p.x & y == p.y;
    }

    public override int GetHashCode()
    { 
        return ShiftAndWrap(x.GetHashCode(), 2) ^ y.GetHashCode();
    } 

    private int ShiftAndWrap(int value, int positions)
    {
        positions = positions & 0x1F;

        // Save the existing bit pattern, but interpret it as an unsigned integer.
        uint number = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
        // Preserve the bits to be discarded.
        uint wrapped = number >> (32 - positions);
        // Shift and wrap the discarded bits.
        return BitConverter.ToInt32(BitConverter.GetBytes((number << positions) | wrapped), 0);
    }
} 

public class Example
{
   public static void Main()
   {
        Point pt = new Point(5, 8);
        Console.WriteLine(pt.GetHashCode());

        pt = new Point(8, 5);
        Console.WriteLine(pt.GetHashCode());
   }
}
// The example displays the following output:
//       28
//       37 

通用 Windows 平台
自 8 起可用
.NET Framework
自 1.1 起可用
可移植类库
可移植 .NET 平台 中受支持
Silverlight
自 2.0 起可用
Windows Phone Silverlight
自 7.0 起可用
Windows Phone
自 8.1 起可用
返回页首
显示: