我来自一个Python世界,其中只有可散列的对象才可以用作字典的键。 C#中是否有类似的限制?您可以使用自定义类型作为字典键吗?
字典键的要求是它是可比较的和可散列的。这在 .NET 中一直是海龟,每种类型(指针类型除外)都派生自 System.Object,并且由于其 Equals() 方法,它总是具有可比性。并且由于其 GetHashCode() 方法而可散列。因此任何 .NET 类型都可以自动用作密钥。
如果你想使用自己的类型作为键,那么你只需要在想要重新定义对象标识时做一些特殊的事情。换句话说,如果您需要两个不同对象相等的能力。然后,您可以重写 Equals() 方法,通常比较对象的字段。然后你还必须重写GetHashCode(),相等的对象必须生成相同的哈希码。
如果类型无法更改,或者您想要自定义行为,特别是字典的行为,那么您可以将自定义 IEqualityComparer<> 传递给构造函数。请记住,您使用自己的 GetHashCode() 生成的哈希码的质量决定了字典的效率。
是的,键的重要之处在于它们实现(或具有良好的默认实现)
GetHashCode
和Equals
。 Dictionary<T, K>
实现可以利用通用 IEqualityComparer<T>
。
所有自定义类型都将附带
GetHashCode
和 Equals
的默认实现,因为它们是 object
的成员,但是,该默认值可能并不总是与您的类型相关。
字典首先尝试获取哈希码来确定值将进入的存储桶。如果存在哈希冲突,它会退回到相等(我认为)。
请注意,您使用的密钥类型(
class
、struct
、原始类型等)可能会产生不同的性能特征。在我们的代码库中,我们发现 GetHashCode
中 struct
的默认实现并不像我们自己重写它那么快。我们还发现,嵌套字典在访问时间方面比具有复合键的单个字典表现更好。
可以,只需实现 IEqualityComparer 接口,重写 GetHashCode 和 Equals 即可。
我想补充一点,现在,您可以使用 C#
record
类型作为键。
record
(而不是class
)的好处是它现在自动为您实现GetHashCode()
等(通过编译器生成的魔法)。