C# 元组(或数组)作为C语言中的字典键#

C# 元组(或数组)作为C语言中的字典键#,c#,dictionary,hashtable,tuples,C#,Dictionary,Hashtable,Tuples,我正在尝试用C#制作一个字典查找表。我需要将3元组值解析为一个字符串。我尝试使用数组作为键,但没有成功,我不知道还能做什么。此时,我正在考虑制作一个字典中的字典,但这看起来可能不太好看,尽管这是我在javascript中的实现方式。我将使用适当的GetHashCode覆盖元组,并将其用作键 只要您重载了正确的方法,您就会看到良好的性能。如果您使用的是.NET 4.0,请使用元组: lookup = new Dictionary<Tuple<TypeA, TypeB, TypeC>

我正在尝试用C#制作一个字典查找表。我需要将3元组值解析为一个字符串。我尝试使用数组作为键,但没有成功,我不知道还能做什么。此时,我正在考虑制作一个字典中的字典,但这看起来可能不太好看,尽管这是我在javascript中的实现方式。

我将使用适当的GetHashCode覆盖元组,并将其用作键


只要您重载了正确的方法,您就会看到良好的性能。

如果您使用的是.NET 4.0,请使用元组:

lookup = new Dictionary<Tuple<TypeA, TypeB, TypeC>, string>();

如果出于某种原因,您真的不想创建自己的元组类,或使用内置在.NET4.0中的元组类,那么还有一种可能的方法;可以将三个键值组合为一个值

例如,如果这三个值都是整数类型,不超过64位,则可以将它们组合成
ulong

最糟糕的情况是,您可以始终使用字符串,只要您确保其中的三个组成部分由某个字符或序列分隔,而该字符或序列不会出现在键的组成部分中,例如,您可以尝试使用三个数字:

string.Format("{0}#{1}#{2}", key1, key2, key3)

这种方法显然有一些合成开销,但取决于您使用它的目的,这可能是微不足道的,不必关心它。

以下是.NET元组供参考:

[Serializable] 
public class Tuple<T1, T2, T3> : IStructuralEquatable, IStructuralComparable, IComparable, ITuple {

    private readonly T1 m_Item1; 
    private readonly T2 m_Item2;
    private readonly T3 m_Item3; 

    public T1 Item1 { get { return m_Item1; } }
    public T2 Item2 { get { return m_Item2; } }
    public T3 Item3 { get { return m_Item3; } } 

    public Tuple(T1 item1, T2 item2, T3 item3) { 
        m_Item1 = item1; 
        m_Item2 = item2;
        m_Item3 = item3; 
    }

    public override Boolean Equals(Object obj) {
        return ((IStructuralEquatable) this).Equals(obj, EqualityComparer<Object>.Default);; 
    }

    Boolean IStructuralEquatable.Equals(Object other, IEqualityComparer comparer) { 
        if (other == null) return false;

        Tuple<T1, T2, T3> objTuple = other as Tuple<T1, T2, T3>;

        if (objTuple == null) {
            return false; 
        }

        return comparer.Equals(m_Item1, objTuple.m_Item1) && comparer.Equals(m_Item2, objTuple.m_Item2) && comparer.Equals(m_Item3, objTuple.m_Item3); 
    }

    Int32 IComparable.CompareTo(Object obj) {
        return ((IStructuralComparable) this).CompareTo(obj, Comparer<Object>.Default);
    }

    Int32 IStructuralComparable.CompareTo(Object other, IComparer comparer) {
        if (other == null) return 1; 

        Tuple<T1, T2, T3> objTuple = other as Tuple<T1, T2, T3>;

        if (objTuple == null) {
            throw new ArgumentException(Environment.GetResourceString("ArgumentException_TupleIncorrectType", this.GetType().ToString()), "other");
        }

        int c = 0;

        c = comparer.Compare(m_Item1, objTuple.m_Item1); 

        if (c != 0) return c; 

        c = comparer.Compare(m_Item2, objTuple.m_Item2);

        if (c != 0) return c; 

        return comparer.Compare(m_Item3, objTuple.m_Item3); 
    } 

    public override int GetHashCode() { 
        return ((IStructuralEquatable) this).GetHashCode(EqualityComparer<Object>.Default);
    }

    Int32 IStructuralEquatable.GetHashCode(IEqualityComparer comparer) { 
        return Tuple.CombineHashCodes(comparer.GetHashCode(m_Item1), comparer.GetHashCode(m_Item2), comparer.GetHashCode(m_Item3));
    } 

    Int32 ITuple.GetHashCode(IEqualityComparer comparer) {
        return ((IStructuralEquatable) this).GetHashCode(comparer); 
    }
    public override string ToString() {
        StringBuilder sb = new StringBuilder();
        sb.Append("("); 
        return ((ITuple)this).ToString(sb);
    } 

    string ITuple.ToString(StringBuilder sb) {
        sb.Append(m_Item1); 
        sb.Append(", ");
        sb.Append(m_Item2);
        sb.Append(", ");
        sb.Append(m_Item3); 
        sb.Append(")");
        return sb.ToString(); 
    } 

    int ITuple.Size { 
        get {
            return 3;
        }
    } 
}
[可序列化]
公共类元组:IsstructuralEquatable、IsstructuralComparable、IComparable、ITuple{
专用只读T1 m_项1;
专用只读T2 m_项2;
专用只读T3 m_项目3;
公共T1项1{get{return m_Item1;}}
公共T2项2{get{return m_Item2;}}
公共T3项3{get{return m_Item3;}}
公共元组(T1项1、T2项2、T3项3){
m_Item1=Item1;
m_Item2=Item2;
m_Item3=Item3;
}
公共覆盖布尔等于(对象obj){
返回((IsStructuralEquatable)this).Equals(obj,EqualityComparer.Default);;
}
布尔IStructuralEquatable.Equals(对象其他,IEqualityComparer比较器){
if(other==null)返回false;
Tuple objTuple=其他为Tuple;
if(objTuple==null){
返回false;
}
返回comparer.Equals(m_Item1,objTuple.m_Item1)和&comparer.Equals(m_Item2,objTuple.m_Item2)和&comparer.Equals(m_Item3,objTuple.m_Item3);
}
Int32 IComparable.CompareTo(对象对象){
返回((IStructuralComparable)this.CompareTo(obj,Comparer.Default);
}
Int32 IStructuralComparable.CompareTo(对象其他,IComparer比较器){
if(other==null)返回1;
Tuple objTuple=其他为Tuple;
if(objTuple==null){
抛出新的ArgumentException(Environment.GetResourceString(“ArgumentException\u TupleIncorrectType”,this.GetType().ToString(),“other”);
}
int c=0;
c=比较器.Compare(m_Item1,objTuple.m_Item1);
如果(c!=0)返回c;
c=比较器.Compare(m_Item2,objTuple.m_Item2);
如果(c!=0)返回c;
返回comparer.Compare(m_Item3,objTuple.m_Item3);
} 
公共重写int GetHashCode(){
返回((IsStructuralEquatable)this).GetHashCode(EqualityComparer.Default);
}
Int32 IStructuralEquatable.GetHashCode(IEqualityComparer比较器){
返回Tuple.combinehashcode(comparer.GetHashCode(m_Item1)、comparer.GetHashCode(m_Item2)、comparer.GetHashCode(m_Item3));
} 
Int32 ITuple.GetHashCode(IEqualityComparer比较器){
返回((IsStructuralEquatable)this).GetHashCode(比较器);
}
公共重写字符串ToString(){
StringBuilder sb=新的StringBuilder();
某人加上(“”);
把这个还给(某人);
} 
字符串ITuple.ToString(StringBuilder sb){
sb.附加(m_第1项);
某人加上(“,”);
sb.附加(m_第2项);
某人加上(“,”);
某人附加(m_第3项);
某人加上(“)”;
使某人返回字符串();
} 
int ITuple.Size{
得到{
返回3;
}
} 
}

如果您的消费代码可以使用IDictionary接口,而不是字典,那么我的直觉是使用带有自定义数组比较器的SortedDictionary,即:

class ArrayComparer<T> : IComparer<IList<T>>
    where T : IComparable<T>
{
    public int Compare(IList<T> x, IList<T> y)
    {
        int compare = 0;
        for (int n = 0; n < x.Count && n < y.Count; ++n)
        {
            compare = x[n].CompareTo(y[n]);
        }
        return compare;
    }
}
class ArrayComparer:IComparer
其中T:i可比较
{
公共整数比较(IList x,IList y)
{
int比较=0;
对于(int n=0;n
并由此创建(为了具体示例,使用int[]):

var dictionary=newsorteddictionary(newarraycomparer());

在基于元组和嵌套字典的方法之间,几乎总是最好使用基于元组的方法

从可维护性的角度来看

  • 实现如下功能更容易:

    var myDict = new Dictionary<Tuple<TypeA, TypeB, TypeC>, string>();
    
    因此,在dictionary类中公开必要的索引器,该类在内部处理插入和查找

  • 另外,实现一个合适的
    IEnumerable
    接口,并提供一个
    Add(TypeA、TypeB、TypeC、string)
    方法,该方法将为您提供集合初始值设定项语法,如:

    new MultiKeyDictionary<TypeA, TypeB, TypeC, string> 
    { 
        { a, b, c, null }, 
        ...
    };
    
    新的多键字典
    { 
    {a,b,c,null},
    ...
    };
    

  • 好的、干净的、快速的、容易阅读的方法是:

    • 为当前类型生成相等成员(Equals()和GetHashCode())方法。像这样的工具不仅可以创建方法,还可以为相等性检查和/或计算哈希代码生成必要的代码。生成的代码将比元组实现更优化
    • 只需创建一个从元组派生的简单键类
    添加类似的内容:

    public sealed class myKey : Tuple<TypeA, TypeB, TypeC>
    {
        public myKey(TypeA dataA, TypeB dataB, TypeC dataC) : base (dataA, dataB, dataC) { }
    
        public TypeA DataA => Item1; 
    
        public TypeB DataB => Item2;
    
        public TypeC DataC => Item3;
    }
    
    公共密封
    
    var myDict = new Dictionary<TypeA, Dictionary<TypeB, Dictionary<TypeC, string>>>();
    
    string foo = dict[a, b, c]; //lookup
    dict[a, b, c] = ""; //update/insertion
    
    new MultiKeyDictionary<TypeA, TypeB, TypeC, string> 
    { 
        { a, b, c, null }, 
        ...
    };
    
    public sealed class myKey : Tuple<TypeA, TypeB, TypeC>
    {
        public myKey(TypeA dataA, TypeB dataB, TypeC dataC) : base (dataA, dataB, dataC) { }
    
        public TypeA DataA => Item1; 
    
        public TypeB DataB => Item2;
    
        public TypeC DataC => Item3;
    }
    
    var myDictinaryData = new Dictionary<myKey, string>()
    {
        {new myKey(1, 2, 3), "data123"},
        {new myKey(4, 5, 6), "data456"},
        {new myKey(7, 8, 9), "data789"}
    };
    
    var dict = new Dictionary<(int PersonId, int LocationId, int SubjectId), string>();
    dict.Add((3, 6, 9), "ABC");
    dict.Add((PersonId: 4, LocationId: 9, SubjectId: 10), "XYZ");
    var personIds = dict.Keys.Select(k => k.PersonId).Distinct().ToList();