C# 元组(或数组)作为C语言中的字典键#
我正在尝试用C#制作一个字典查找表。我需要将3元组值解析为一个字符串。我尝试使用数组作为键,但没有成功,我不知道还能做什么。此时,我正在考虑制作一个字典中的字典,但这看起来可能不太好看,尽管这是我在javascript中的实现方式。我将使用适当的GetHashCode覆盖元组,并将其用作键C# 元组(或数组)作为C语言中的字典键#,c#,dictionary,hashtable,tuples,C#,Dictionary,Hashtable,Tuples,我正在尝试用C#制作一个字典查找表。我需要将3元组值解析为一个字符串。我尝试使用数组作为键,但没有成功,我不知道还能做什么。此时,我正在考虑制作一个字典中的字典,但这看起来可能不太好看,尽管这是我在javascript中的实现方式。我将使用适当的GetHashCode覆盖元组,并将其用作键 只要您重载了正确的方法,您就会看到良好的性能。如果您使用的是.NET 4.0,请使用元组: lookup = new Dictionary<Tuple<TypeA, TypeB, TypeC>
只要您重载了正确的方法,您就会看到良好的性能。如果您使用的是.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());
在基于元组和嵌套字典的方法之间,几乎总是最好使用基于元组的方法
从可维护性的角度来看
- 实现如下功能更容易:
因此,在dictionary类中公开必要的索引器,该类在内部处理插入和查找var myDict = new Dictionary<Tuple<TypeA, TypeB, TypeC>, string>();
- 另外,实现一个合适的
接口,并提供一个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();