如何实现多个GetHashCode方法?
How to implement multiple GetHashCode methods?
我有一个定义复合键的接口:
public interface IKey : IEquatable<IKey>
{
public bool KeyPart1 { get; }
public uint KeyPart2 { get; }
int GetHashCode(); // never gets called
}
我有一个对象(带有 ID),我想向其添加复合键接口:
public class MyObject: IEquatable<MyObject>, IKey
{
public MyObject(int i, (bool keyPart1, uint keyPart2) key) {
{
Id=i;
KeyPart1 = key.keyPart1;
KeyPart2 = key.keyPart2;
}
public int Id { get; }
public bool KeyPart1 { get; }
public uint KeyPart2 { get; }
public bool Equals(MyObject other) => this.Id == other.Id;
public override bool Equals(object other) => other is MyObject o && Equals(o);
public override int GetHashCode() => Id.GetHashCode();
bool IEquatable<IKey>.Equals(IKey other) => this.KeyPart1 == other.KeyPart1
&& this.KeyPart2 == other.KeyPart2;
int IKey.GetHashCode() => (KeyPart1, KeyPart2).GetHashCode(); // never gets called
}
但是,当拥有这些对象的列表并尝试使用界面对它们进行分组时,分组失败:
var one = new MyObject(1, (true, 1));
var two = new MyObject(2, (true, 1));
var three = new MyObject(1, (false, 0));
var items = new[] { one, two, three };
var byId = items.GroupBy(i => i);
// result: { [one, three] }, { [two] } -- as expected
var byKey = items.GroupBy<MyObject, IKey>(i => i as IKey);
// result: { [one, two, three] } // not grouped (by 'id' or 'key')
// expected: { [one, two] }, { [three] }
我预计 byId
会将项目按 Id
属性 分组,而 byKey
会将项目按 [=16= 分组] 属性.
然而,byKey
根本没有分组。似乎总是使用覆盖 GetHashCode()
方法,而不是显式实现的接口方法。
是否可以实现这样的东西,其中被分组的项目的类型决定了要使用的散列方法(避免EqualityComparer
)?
我在将转换对象传递给另一个需要 IEnumerable<IKey>
的方法时注意到了这个问题。我有几种不同的类型实现了 IKey
,而那些具有现有 GetHashCode()
方法的类型不起作用,而其他的则起作用。
请注意,这里的对象已经被简化,我无法轻易更改接口(例如,改用 ValueTuple
)。
相等中使用的GetHashCode()
是:
- 通过
object.GetHashCode()
定义的,如果没有提供相等比较器
IEqualityComparer<T>.GetHashCode(T)
,如果提供相等比较器
在您自己的接口上添加您自己的 GetHashCode()
方法没有任何作用,并且永远不会被使用,因为它不是 framework/library 代码知道的 API 的一部分.
所以,我会忘记 IKey.GetHashCode()
,并且(或两者):
- 使
MyObject.GetHashCode()
提供您需要的功能,或者
- 为
MyObject
实例 单独提供自定义相等比较器
对于第二个选项,GroupBy
的重载接受 IEqualityComparer<TKey>
。
我有一个定义复合键的接口:
public interface IKey : IEquatable<IKey>
{
public bool KeyPart1 { get; }
public uint KeyPart2 { get; }
int GetHashCode(); // never gets called
}
我有一个对象(带有 ID),我想向其添加复合键接口:
public class MyObject: IEquatable<MyObject>, IKey
{
public MyObject(int i, (bool keyPart1, uint keyPart2) key) {
{
Id=i;
KeyPart1 = key.keyPart1;
KeyPart2 = key.keyPart2;
}
public int Id { get; }
public bool KeyPart1 { get; }
public uint KeyPart2 { get; }
public bool Equals(MyObject other) => this.Id == other.Id;
public override bool Equals(object other) => other is MyObject o && Equals(o);
public override int GetHashCode() => Id.GetHashCode();
bool IEquatable<IKey>.Equals(IKey other) => this.KeyPart1 == other.KeyPart1
&& this.KeyPart2 == other.KeyPart2;
int IKey.GetHashCode() => (KeyPart1, KeyPart2).GetHashCode(); // never gets called
}
但是,当拥有这些对象的列表并尝试使用界面对它们进行分组时,分组失败:
var one = new MyObject(1, (true, 1));
var two = new MyObject(2, (true, 1));
var three = new MyObject(1, (false, 0));
var items = new[] { one, two, three };
var byId = items.GroupBy(i => i);
// result: { [one, three] }, { [two] } -- as expected
var byKey = items.GroupBy<MyObject, IKey>(i => i as IKey);
// result: { [one, two, three] } // not grouped (by 'id' or 'key')
// expected: { [one, two] }, { [three] }
我预计 byId
会将项目按 Id
属性 分组,而 byKey
会将项目按 [=16= 分组] 属性.
然而,byKey
根本没有分组。似乎总是使用覆盖 GetHashCode()
方法,而不是显式实现的接口方法。
是否可以实现这样的东西,其中被分组的项目的类型决定了要使用的散列方法(避免EqualityComparer
)?
我在将转换对象传递给另一个需要 IEnumerable<IKey>
的方法时注意到了这个问题。我有几种不同的类型实现了 IKey
,而那些具有现有 GetHashCode()
方法的类型不起作用,而其他的则起作用。
请注意,这里的对象已经被简化,我无法轻易更改接口(例如,改用 ValueTuple
)。
相等中使用的GetHashCode()
是:
- 通过
object.GetHashCode()
定义的,如果没有提供相等比较器 IEqualityComparer<T>.GetHashCode(T)
,如果提供相等比较器
在您自己的接口上添加您自己的 GetHashCode()
方法没有任何作用,并且永远不会被使用,因为它不是 framework/library 代码知道的 API 的一部分.
所以,我会忘记 IKey.GetHashCode()
,并且(或两者):
- 使
MyObject.GetHashCode()
提供您需要的功能,或者 - 为
MyObject
实例 单独提供自定义相等比较器
对于第二个选项,GroupBy
的重载接受 IEqualityComparer<TKey>
。