【问题标题】:How to implement multiple GetHashCode methods?如何实现多个 GetHashCode 方法?
【发布时间】:2021-07-21 07:38:56
【问题描述】:

我有一个定义复合键的接口:

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 将拥有由Key 属性分组的项目。

但是,byKey 根本没有分组。似乎总是使用覆盖GetHashCode() 方法,而不是显式实现的接口方法。

是否有可能实现这样的事情,其中​​被分组的项目的类型决定了要使用的哈希方法(避免EqualityComparer)?

我在将转换对象传递给另一个需要IEnumerable&lt;IKey&gt; 的方法时注意到了这个问题。我有几种不同的类型实现了IKey,而那些具有现有GetHashCode() 方法的类型不起作用,而其他类型则起作用。

请注意这里的对象已被简化,我无法轻易更改接口(例如,改用ValueTuple)。

【问题讨论】:

  • 为什么要避免使用 EqualityComparer?我认为,只要您想要以多种方式比较对象,这就是您要走的路。
  • 无关提示:Equals 应该预期null,即=&gt; other is not null &amp;&amp; this.KeyPart1 == other.KeyPart1 &amp;&amp; this.KeyPart2 == other.KeyPart2;;您还可以在object 版本中使用偷偷摸摸的短路:public override bool Equals(object other) =&gt; other is MyObject typed &amp;&amp; Equals(typed);
  • @JonasH 只是因为它“出乎意料”;我实际上将对象(转换为IKey)传递给另一个方法,该方法采用IKeys(来自不同的实现)并且通常在没有EqualityComparer的情况下工作正常。

标签: c# hashcode


【解决方案1】:

用于相等的GetHashCode() 是:

  • 如果没有提供相等比较器,则通过object.GetHashCode() 定义的那个
  • IEqualityComparer&lt;T&gt;.GetHashCode(T),如果提供了相等比较器

在您自己的接口上添加您自己的GetHashCode() 方法没有任何作用,并且永远不会被使用,因为它不是框架/库代码知道的 API 的一部分。

所以,我会忘记 IKey.GetHashCode() 和其中一个(或两者):

  • 使MyObject.GetHashCode() 提供您需要的功能,或者
  • MyObject 实例单独提供自定义相等比较器

对于第二个选项,GroupBy 的重载接受 IEqualityComparer&lt;TKey&gt;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-07-22
    • 1970-01-01
    • 2017-02-08
    • 1970-01-01
    • 2021-04-18
    • 2011-04-23
    • 2023-03-24
    相关资源
    最近更新 更多