【问题标题】:What can you use as keys in a C# dictionary?您可以将什么用作 C# 字典中的键?
【发布时间】:2013-09-10 06:38:51
【问题描述】:

我来自 python 世界,其中只有可散列的对象可以用作字典的键。 C#中是否有类似的限制?可以使用自定义类型作为字典键吗?

【问题讨论】:

  • 理论上你可以使用任何类型作为键。在实践中它应该是一个不可变的类型,否则当键开始变异时你会陷入痛苦的世界......
  • @PatrykĆwiek Note this comment -- “只有 GetHashCode 值和 Equals 定义的等价类需要是不可变的。为所有类类型定义了合适的等价类——引用相等——即通常是唯一应该用于可变对象的等价类。如果字典的目的是将该类型的实例与其他东西相关联,那么将可变类类型作为字典键没有任何问题。“

标签: c# dictionary types custom-type


【解决方案1】:

对字典键的要求是它是可比较的和可散列的。这是 .NET 中的乌龟,每种类型(指针类型除外)都派生自 System.Object,并且由于其 Equals() 方法,它始终具有可比性。并且由于它的 GetHashCode() 方法而可以散列。因此,任何 .NET 类型都可以自动用作键。

如果你想使用你自己的类型作为键,那么你只需要做一些特殊的事情,如果你想重新定义对象标识。换句话说,如果您需要两个不同对象相等的能力。然后,您将覆盖 Equals() 方法,通常比较对象的字段。然后你还必须重写 GetHashCode(),相等的对象必须生成相同的哈希码。

如果无法更改类型,或者您想为 Dictionary 自定义行为,那么您可以将自定义 IEqualityComparer 传递给构造函数。请记住,您使用自己的 GetHashCode() 生成的哈希码的质量决定了字典的效率。

【讨论】:

  • 也许比“可比”更好的词是equitable
【解决方案2】:

可以,只需实现接口 IEqualityComparer,覆盖 GetHashCode 和 Equals。

【讨论】:

  • 所以你必须自己做这一切?有什么东西可以帮助你实现散列函数吗?
  • GetHasCode 是一个有趣的方法名称。默认实现是return false; :)
  • @static_rtti: System.Object 有一个 GetHashCode() 的默认实现,它可能只是基于对象的地址(或者在垃圾收集的情况下可能是某种 ID)。其他一些对象,例如System.String,会覆盖它以返回与其内容相关的内容。对于所有其他情况,您可以使用IEqualityComparer 的实现来自定义散列。以 StringComparer 类为例:它的每个成员都有不同的 IEqualityComparer 实现来匹配字符串比较,因此您可以使用不区分大小写的散列。
  • @Suhan 你实际上并不需要实现那个接口,但这可以说是一件好事。
  • 当我不得不实现IEqualtyComparer 时,我通常只需将GetHashCode 调用委托给其他东西,比如标识符或字符串。我很少需要自己生成哈希码。显然,这取决于所存储的对象。
【解决方案3】:

是的,键的重要之处在于它们实现(或具有良好的默认实现)GetHashCodeEqualsDictionary<T, K> 实现可以利用通用 IEqualityComparer<T>

所有自定义类型都将附带GetHashCodeEquals 的默认实现,因为它们是object 的成员,但是,该默认值可能并不总是与您的类型相关。

字典首先尝试获取哈希码以确定值将落入的存储桶。如果存在哈希冲突,它会退回到相等(我认为)。

请注意,您使用的密钥类型(classstruct、原始类型等)会产生不同的性能特征。在我们的代码库中,我们发现structGetHashCode 的默认实现不如我们自己覆盖它快。我们还发现,嵌套字典在访问时间方面的性能优于具有复合键的单个字典。

【讨论】:

  • 我想如果您使用明显的相等定义(即,如果所有字段相等,则对象相等),默认的 Equals 和 GetHashCode 就足够了吗? (除了可能出于性能原因,但我还没有)
  • @static_rtti class 相等的默认实现不是字段相等,我认为它是引用相等。对于struct,我相信它的值等于第一个 x 字节块或类似的东西。就我个人而言,我喜欢确保将自定义类型作为键很好地定义实现,但同时我们很少有自定义类型是键的实例。
  • @static_rtti 不一定,所有引用类型默认都使用引用相等。
  • 你说得对性能,值类型 GetHashCode 默认实现使用反射来获取类型字段的值 (msdn.microsoft.com/en-us/library/system.object.gethashcode.aspx)。它很慢。另一方面,它为您提供了非常好的默认行为。
  • @CharlesBarthélemy 是的,默认值实际上正如您所期望的那样,这是幸运的。我从来没有意识到它使用了反射。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-12
  • 2014-04-14
  • 2016-06-03
  • 2013-03-04
  • 2019-06-15
  • 1970-01-01
相关资源
最近更新 更多