我有很多字典,其中的键是几个不同值(主要是字符串和整数)的组合。我是将这些键实现为类(和 overrideGetHashCode()
等Equals()
)还是struct
改用?
ReSharper 使覆盖变得容易,但代码看起来很糟糕。改用结构是否有任何性能影响?
我有很多字典,其中的键是几个不同值(主要是字符串和整数)的组合。我是将这些键实现为类(和 overrideGetHashCode()
等Equals()
)还是struct
改用?
ReSharper 使覆盖变得容易,但代码看起来很糟糕。改用结构是否有任何性能影响?
如果您唯一的问题是定义在 a 中使用的相等性,Dictionary<TKey,TValue>
那么您可以选择的另一条路径是实现IEqualityComparer<T>
. 这可以手动传递给字典构造函数,并在TKey
不修改键类型的情况下处理值的相等比较。
如果您在为复合值定义相等性时遇到更普遍的问题,那么我将专注于使复合值本身支持相等性。是的,定义平等所需的全套方法很痛苦,但它主要是样板代码。做对比样板代码看起来是否混乱更重要。
我实际上会说,对于任何结构,都应该始终手动编写覆盖Equals()
并GetHashCode()
与实现一起编写IEquatable<T>
,如果它很可能被某人用作键,那么我当然不会为了避免这样做而使用它.
除了需要装箱外,默认实现也相当慢,因为它使用反射来检查字段。至少在某些框架版本中还有一个错误(实现非常明智地优化为二进制比较,这样做会给出正确的结果,但不幸的是在这种情况下会误判,因此两个包含等效decimal
字段的结构可能是被认为是不平等的)。
当需要一个除了作为复合键之外对系统没有任何意义的快速复合时,我建议使用Tuple
. Tuple.Create()
使组合它们变得容易,并且对 和 的覆盖Equals()
非常GetHashCode()
合理。
在某些情况下,也可以使用匿名类作为键(当然只在给定方法的上下文中),这里对Equals()
and的覆盖GetHashCode()
也很合理。
要创建这样一个复合类,推荐的技术是继承自Tuple<int, string, ...>
.
这样,您不必重写GetHashCode
和Equals
自己,基类会为您完成。
您可以轻松地为每个字段提供有意义的 get 访问器。
public class CompositeKey : Tuple<string, int>
{
public CompositeKey(string name, int age)
: base(name, age)
{
}
public string Name { get { return Item1; } }
public int Age { get { return Item2; } }
}
这也强制执行不变性,这适用于字典键。
至于性能,内置的元组相当快。我发现自定义结构可以更快,但如果你真的需要每一个额外的性能,最好的方法是将你的关键数据直接编码为 int 或 long。