Question

Je cherche essentiellement un moyen d'accéder à une valeur Hashtable en utilisant une clé typée à deux dimensions en c #.

Finalement je serais capable de faire quelque chose comme ceci

HashTable[1][false] = 5;
int a = HashTable[1][false];
//a = 5

Voici ce que j'ai essayé ... n'a pas fonctionné

Hashtable test = new Hashtable();
test.Add(new Dictionary<int, bool>() { { 1, true } }, 555);
Dictionary<int, bool> temp = new Dictionary<int, bool>() {{1, true}};
string testz = test[temp].ToString(); 
Était-ce utile?

La solution

Je pense qu'une meilleure approche est de résumer les nombreux domaines de votre clé multidimensionnelle dans une classe / struct. Par exemple

struct Key {
  public readonly int Dimension1;
  public readonly bool Dimension2;
  public Key(int p1, bool p2) {
    Dimension1 = p1;
    Dimension2 = p2;
  }
  // Equals and GetHashCode ommitted
}

Maintenant, vous pouvez créer et utiliser une table de hachage normale et utiliser cette enveloppe comme une clé.

Autres conseils

Comment l'utilisation d'un dictionnaire régulier avec une sorte de structure Tuple comme une clé?

public class TwoKeyDictionary<K1,K2,V>
{
    private readonly Dictionary<Pair<K1,K2>, V> _dict;

    public V this[K1 k1, K2 k2]
    {
        get { return _dict[new Pair(k1,k2)]; }
    }

    private struct Pair
    {
        public K1 First;
        public K2 Second;

        public override Int32 GetHashCode()
        {
            return First.GetHashCode() ^ Second.GetHashCode();
        }

        // ... Equals, ctor, etc...
    }
}

Juste au cas où quelqu'un est ici récemment, un exemple de la façon de faire la façon rapide et sale en .Net 4.0, comme décrit par l'un des commentateurs.

class Program
{
  static void Main(string[] args)
  {
     var twoDic = new Dictionary<Tuple<int, bool>, String>();
     twoDic.Add(new Tuple<int, bool>(3, true), "3 and true." );
     twoDic.Add(new Tuple<int, bool>(4, true), "4 and true." );
     twoDic.Add(new Tuple<int, bool>(3, false), "3 and false.");

     // Will throw exception. Item with the same key already exists.
     // twoDic.Add(new Tuple<int, bool>(3, true), "3 and true." );

     Console.WriteLine(twoDic[new Tuple<int, bool>(3,false)]);
     Console.WriteLine(twoDic[new Tuple<int, bool>(4,true)]);
     // Outputs "3 and false." and "4 and true."
  }
}

Vous pouvez le faire en C # 7.0 maintenant avec les nouveaux tuples:

// Declare
var test = new Dictionary<(int, bool), int>();

// Add
test.Add((1, false), 5);

// Get
int a = test[(1, false)];

Je pense que cela pourrait être plus proche de ce que vous cherchez ...

var data = new Dictionary<int, Dictionary<bool, int>>();

Je vous suggère une légère variation sur la solution de jachymko qui vous permettra d'éviter de créer une classe pour des paires de clés. Au lieu de cela, envelopper un dictionnaire privé de dictionnaires, comme ceci:

public class MultiDictionary<K1, K2, V>
{
    private Dictionary<K1, Dictionary<K2, V>> dict = 
        new Dictionary<K1, Dictionary<K2, V>>();

    public V this[K1 key1, K2 key2]
    {
        get
        {
            return dict[key1][key2];
        }

        set
        {
            if (!dict.ContainsKey(key1))
            {
                dict[key1] = new Dictionary<K2, V>();
            }
            dict[key1][key2] = value;
        }
    }
}

vous avez besoin d'une classe de clé pour la dictonary qui implémente GetHashCode correctement. Et vous pouvez étendre dictonary pour vous permettre d'accéder à une manière amicale.

la classe keypair

public class KeyPair<Tkey1, Tkey2>
{
    public KeyPair(Tkey1 key1, Tkey2 key2)
    {
        Key1 = key1;
        Key2 = key2;
    }

    public Tkey1 Key1 { get; set; }
    public Tkey2 Key2 { get; set; }

    public override int GetHashCode()
    {
        return Key1.GetHashCode() ^ Key2.GetHashCode();
    }
    public override bool Equals(object obj)
    {
        KeyPair<Tkey1, Tkey2> o = obj as KeyPair<Tkey1, Tkey2>;
        if (o == null)
            return false;
        else
            return Key1.Equals(o.Key1) && Key2.Equals(o.Key2);
    }
}

étendre Dictonary <>

public class KeyPairDictonary<Tkey1, Tkey2, Tvalue> 
    : Dictionary<KeyPair<Tkey1, Tkey2>, Tvalue>
{
    public Tvalue this[Tkey1 key1, Tkey2 key2]
    {
        get
        {
            return this[new KeyPair<Tkey1, Tkey2>(key1, key2)];
        }
        set
        {
            this[new KeyPair<Tkey1, Tkey2>(key1, key2)] = value;
        }
    }
}

et que vous utilisez comme ceci

        KeyPairDictonary<int, bool, string> dict = 
            new KeyPairDictonary<int, bool, string>();
        dict[1, false] = "test";
        string test = dict[1, false];

Je suggère que vous créez une petite classe personnalisée exposant les propriétés bool et int, et passer outre ses GetHashCode et equals méthodes, puis utilisez cela comme la clé.

Essentiellement, vous devez utiliser un Hashtable intégré. Si vous pensez à votre question, une table de hachage avec deux clés est une fonction avec deux indépendants variables et f(x,y) est 2 dimensions par définition.

Mais vous voulez l'utiliser comme il était un Hashtable, et non hash embarqués. Donc ce que vous devez faire est de créer un objet qui entoure cette idée Hashtable intégrée et fonctionne comme un hachage.

Quelques accrocs:

  • Vous voulez itérer, donc vous devez remplacer la méthode GetEnumerator(). Et vous avez besoin de votre propre itérateur qui itérer correctement en 2 dimensions.
  • Vous devez faire plus pour être sûr de vérifier qu'il n'y a pas de doublons.

J'ai inclus mon code pour le faire:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Windows.Forms;

namespace YourProjectNameHere
{
    public class Hashtable2D
    {
        /// <summary>
        /// This is a hashtable of hashtables
        /// The X dim is the root key, and the y is the internal hashes key
        /// </summary>
        /// 
        private Hashtable root = new Hashtable();
        public bool overwriteDuplicates = false;
        public bool alertOnDuplicates = true;

        public void Add(object key_x, object key_y, object toStore)
        {
            if(root[key_x]!=null)//If key_x has already been entered 
            {
                Hashtable tempHT = (Hashtable)root[key_x];//IF the hash table does not exist then focus will skip to the catch statement
                if (tempHT[key_y] == null)  tempHT.Add(key_y, toStore);
                else handleDuplicate(tempHT, key_y, toStore);
            }else{//Making a new hashtable 
                Hashtable tempHT = new Hashtable();
                tempHT.Add(key_y, toStore);
                root.Add(key_x, tempHT);
            }

        }

        public void Remove(object key_x, object key_y)
        {
            try{
                ((Hashtable)root[key_x]).Remove(key_y);
            }catch(Exception e){
                MessageBox.Show("That item does not exist");
            }

        }

        public void handleDuplicate (Hashtable tempHT, object key_y, object toStore)
        {
            if (alertOnDuplicates) MessageBox.Show("This Item already Exists in the collection");

            if (overwriteDuplicates)
            {
                tempHT.Remove(key_y);
                tempHT.Add(key_y,toStore);
            }
        }

        public object getItem(object key_x, object key_y)
        {
            Hashtable tempHT = (Hashtable)root[key_x];
            return tempHT[key_y];
        }

        public ClassEnumerator GetEnumerator()
        {
            return new ClassEnumerator(root);
        }

        public class ClassEnumerator : IEnumerator
        {
            private Hashtable ht;
            private IEnumerator iEnumRoot;
            private Hashtable innerHt;
            private IEnumerator iEnumInner;

            public ClassEnumerator(Hashtable _ht)
            {
                ht = _ht;
                iEnumRoot = ht.GetEnumerator();

                iEnumRoot.MoveNext();//THIS ASSUMES THAT THERE IS AT LEAST ONE ITEM

                innerHt = (Hashtable)((DictionaryEntry)iEnumRoot.Current).Value;
                iEnumInner = innerHt.GetEnumerator();
            }

            #region IEnumerator Members

            public void Reset()
            {
                iEnumRoot = ht.GetEnumerator();
            }

            public object Current
            {
                get
                {
                    return iEnumInner.Current; 
                }
            }

            public bool MoveNext()
            {
                if(!iEnumInner.MoveNext())
                {
                    if (!iEnumRoot.MoveNext()) return false;
                    innerHt = (Hashtable)((DictionaryEntry)iEnumRoot.Current).Value;
                    iEnumInner = innerHt.GetEnumerator();
                    iEnumInner.MoveNext();
                }
                return true;
            }

            #endregion
        }

    }
}

Vous pourriez être en mesure de « double nid » vos hashtables - en d'autres termes, votre Dictionnaire principal est de type Dictionary<int, Dictionary<bool, my_return_type>>

.

qui permet d'atteindre votre objectif d'être en mesure d'utiliser la double notation de support dans votre premier extrait de code.

Bien sûr, le côté de la direction est un peu plus délicat. Chaque fois que vous ajoutez une entrée, vous devez tester si le dictionnaire principal contient un dictionnaire pour la clé primaire, et ajouter un nouveau dictionnaire sinon, puis ajoutez la clé secondaire et la valeur au dictionnaire intérieur.

Pouvez-vous utiliser un Dictionary<KeyValuePair<int,bool>,int>?

Enveloppez votre clé à deux dimensions dans un type séparé et utiliser ce type comme une clé. Voir également impérieuses méthodes de GetHashCode() et Equals(). Utiliser de préférence Dictionary<> au lieu de HashTable puisque, apparemment, vous pouvez l'utiliser.

Voici un exemple , vous pouvez utiliser une Hashtable normale au lieu de celui que j'utilisé.

Une façon rapide et sale serait de créer une clé composite à partir des deux éléments d'information, par exemple.

IDictionary<string, int> values = new Dictionary<string, int>();
int i = ...;
bool b = ...;
string key = string.Concat(i, '\0', b);
values[key] = 555;

Pour résumer cela un peu mieux vous pouvez envelopper le dictionnaire:

public class MyDict
{
    private readonly IDictionary<string, int> values = new Dictionary<string, int>();

    public int this[int i, bool b]
    {
        get
        {
            string key = BuildKey(i, b);
            return values[key];
        }

        set
        {
            string key = BuildKey(i, b);
            values[key] = value;
        }
    }

    private static string BuildKey(int i, bool b)
    {
        return string.Concat(i, '\0', b);
    }
}

Pour rendre plus robuste, encapsuler la clef composite en tant que type, par exemple, une classe qui contient les deux champs, vous assurant la priorité sur les Equals () et GetHashCode () correctement.

Regardez, ce code fonctionne très bien:

    public Form1()
    {
            InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {

        this.Services = new Dictionary<object, Hashtable>();
        this.Services.Add("array1", new Hashtable());

        this.Services["array1"]["qwe"] = "123";
        this.Services["array1"][22] = 223;

        object zz = null;
        zz = this.Services["array1"]["qwe"];
        MessageBox.Show(zz.ToString()); // shows qwe

        zz = this.Services["array1"][22];
        MessageBox.Show(zz.ToString()); // shows 22
    }

Maintenant, nous avons juste besoin d'une enveloppe pour éviter de faire manuellement this.Services.Add ( "array1", new Hashtable ());

Je pense que la meilleure façon de le faire est maintenant d'utiliser Tupple.Create et ValueTuple.Create:

> var k1 =  Tuple.Create("test", int.MinValue, DateTime.MinValue, double.MinValue);
> var k2 = Tuple.Create("test", int.MinValue, DateTime.MinValue, double.MinValue);
> var dict = new Dictionary<object, object>();
> dict.Add(k1, "item");
> dict.Add(k2, "item");
An item with the same key has already been added....
> dict[k1] == dict[k2]
true

ou utiliser un nouveau c # 7 syntaxe tuple pour créer des clés-uplet:

var k = (item1: "value1", item2: 123);

Ceci est mon implémentation Dictionnaire imbriqué:

public class TwoKeysDictionary<K1, K2, T>:
        Dictionary<K1, Dictionary<K2, T>>
{
    public T this[K1 key1, K2 key2]
    {
        get => base.ContainsKey(key1) && base[key1].ContainsKey(key2) ? base[key1][key2] : default;
        set
        {
            if (ContainsKey(key1) && base[key1].ContainsKey(key2))
                base[key1][key2] = value;
            else
                Add(key1, key2, value);
        }
    }

    public void Add(K1 key1, K2 key2, T value)
    {
        if (ContainsKey(key1))
        {
            if (base[key1].ContainsKey(key2))
                throw new Exception("Couple " + key1 + "/" + key2 + " already exists!");
            base[key1].Add(key2, value);
        }
        else
            Add(key1, new Dictionary<K2, T>() { { key2, value } });
    }

    public bool ContainsKey(K1 key1, K2 key2) => ContainsKey(key1) && base[key1].ContainsKey(key2);
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top