【问题标题】:Interchangable Multikey Dictionary可互换的多键字典
【发布时间】:2018-09-06 18:50:19
【问题描述】:

我有许多实体需要在它们之间共享数据。两个实体都会从这个字典中请求值。

public class Key {
    public string nameA;
    public string nameB;
}
public class SharedValue {
    public int id;
}

private Dictionary<Key, SharedValue> relation = new Dictionary<Key, SharedValue>();

然后添加到字典中。

relation.Add(new Key(){ nameA = "User1", nameB = "User2" }, new SharedValue(){ id = -11 });

最后,我希望无论 nameA 或 nameB 的顺序如何,我都能得到一个共享的 SharedValue。

relation[new Key(){ nameA = "User1", nameB = "User2" }].id  // Get -11
relation[new Key(){ nameA = "User2", nameB = "User1" }].id  // Get -11

【问题讨论】:

  • 你能解释一下你的问题是什么吗?
  • 只要Key 不覆盖Equals()GetHashCode(),一个标准实现将用于为您的Dictionary 生成哈希码

标签: c# dictionary


【解决方案1】:

为您的密钥类定义 EqualsGeyHashCode 方法:

    public class Key
    {
        public string nameA;
        public string nameB;

        public override bool Equals(object obj)
        {
            return base.Equals(obj as Key);
        }

        protected bool Equals(Key other)
        {
            return string.Equals(nameA, other.nameA) && string.Equals(nameB, other.nameB) ||
                   string.Equals(nameA, other.nameB) && string.Equals(nameB, other.nameA);
        }

        public override int GetHashCode()
        {
            return (nameA?.GetHashCode() ^ nameB?.GetHashCode()) ?? 0;
        }
    }

【讨论】:

  • 字典键所必需的不是Equals,而是GetHashCode,然后是Equals(总是两者)
  • 对于平等比较,我认为IEquatable&lt;Key&gt; 是一个更好的选择。覆盖的 Equals 和 GetHashCode 可以定向到 IEquatable&lt;Key&gt;.Equals(Key k)IEquatable&lt;Key&gt;.GetHashCode()
【解决方案2】:

您可以提供一个自定义 IEqualityComparer&lt;Key&gt;,您可以将其用于字典构造函数(以及许多 LINQ 方法):

public class UnorderedKeyComparer : IEqualityComparer<Key>
{
    public bool Equals(Key x, Key y)
    {
        if (object.ReferenceEquals(x, y))
            return true;
        if (x == null || y == null)
            return false;
        if (string.Equals(x.NameA, y.NameA) && string.Equals(x.NameB, y.NameB))
            return true;
        if (string.Equals(x.NameA, y.NameB) && string.Equals(x.NameB, y.NameA))
            return true;
        return false;
    }

    public int GetHashCode(Key obj)
    {
        return (obj?.NameA?.GetHashCode() ?? int.MinValue) ^ (obj?.NameB?.GetHashCode() ?? int.MinValue);
    }
}

所以在这种情况下,您只需要使用 UnorderedKeyComparer 初始化字典:

Dictionary<Key, SharedValue> Relation = new Dictionary<Key, SharedValue>(new UnorderedKeyComparer());

顺便说一句,我忍不住要解决你的命名问题。

【讨论】:

  • 一个 n1 解决方案 :) 恕我直言,覆盖 EqualsGetHashCode 对于这项任务来说有点矫枉过正,而且它可能会导致问题......
  • 别担心,我得看看正确的格式。
  • 在我看来使用IEquatable&lt;Key&gt; 是一个更好的选择。 EqualityComparer&lt;Key&gt; 需要明确提供。事实上,我们可以将对象类覆盖的方法定向到 IEquatable 实现
  • @MrinalKamboj:没有好坏之分,两者各有利弊。如果您不想比较无序的键怎么办?如果您覆盖Equals(或实现IEquatable&lt;&gt;),您将无法再更改此行为,永远不会。 IEqualityComparer&lt;&gt; 使您能够在此处提供一个比较器,并在其他地方提供另一种类型。它是可重用的,无需破坏现有代码或固定Equals 实现。您可以使用上面的代码轻松实现IEquatable&lt;Key&gt;,我只是想提供最灵活和最轻量级的方法。
  • @TimSchmelter 我明白你的意思,但是 IEquatable 就像一个默认实现,如果我们可以访问源代码并且这仍然不会阻止我们使用 IEqualityComparer。在某个地方期望最终用户提供默认相等性并不总是很好的预兆,并且主要委托给 Object.Equals
【解决方案3】:

您有很多选择,例如:

  1. 实现IEqualityComparer&lt;YourKey&gt; 并传递一个实例 类将IEqualityComparer&lt;YourKey&gt; 实现为 字典
  2. 仅在您的自定义键上实现 IEquatable&lt;T&gt;

这是怎么做的Option 2:

public class Key:IEquatable<Key> {
    public string nameA;
    public string nameB;

    public bool Equals(Key other)
    {
        if(other == null)
            return false;
        if(ReferenceEquals(this,other))
            return true;
        return (string.Equals(this.nameA,other.nameA) && string.Equals(this.nameB,other.nameB))
                || (string.Equals(this.nameA,other.nameB) && string.Equals(this.nameB,other.nameA));
    }
    public override bool Equals(object obj){
        if(obj == null)
            return false;
        if(ReferenceEquals(obj,this))
            return true;
        return Equals((Key)obj);
    }
    public override int GetHashCode(){
        return (nameA?.GetHashCode() ^ nameB?.GetHashCode()) ?? 0;
    }
 }

测试:

private static Dictionary<Key, SharedValue> relation = new Dictionary<Key, SharedValue>();

static void Main(string[] args)
{
    relation.Add(new Key{nameA="a",nameB="b"},new SharedValue{id=1});
    if(relation.ContainsKey(new Key{nameA="a",nameB="b"})){
        Console.WriteLine("YES");
    }
    if(relation.ContainsKey(new Key{nameA="b",nameB="a"})){
        Console.WriteLine("YES");
    }
    else Console.WriteLine("NO");
}

输出:

YES
YES

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-01-25
    • 2020-06-17
    • 1970-01-01
    • 2013-04-14
    • 1970-01-01
    • 2021-09-01
    • 2016-04-14
    • 2015-12-13
    相关资源
    最近更新 更多