Как использовать составной ключ для словаря?

#c# #.net #generics #dictionary

#c# #.net #общие сведения #словарь

Вопрос:

Мне нужно создать что-то вроде словаря, где ключом была бы пара enum и int, а значением — object. Итак, я хочу сопоставить пару с некоторым объектом.

Одним из вариантов было бы

 public enum SomeEnum
{
 value1, value2
}

class Key
{
  public SomeEnum;
  public int counter;  

  // Do I have to implement Compare here?
}

Dictionary<SomeEnum, object> _myDictionary;
  

Другим вариантом было бы преобразовать enum и int в некоторый уникальный ключ.

 string key = String.Format("{0}/{1}", enumValue, intValue)
  

Такой подход требует синтаксического анализа строк, много дополнительной работы.

Как сделать это легко?

Комментарии:

1. Какую версию C # вы используете?

2. Visual Studio 2008, поэтому я думаю, что это 3.5

Ответ №1:

Я бы выбрал что-то похожее на

 public enum SomeEnum
{
 value1, value2
}

public struct Key
{
  public SomeEnum;
  public int counter;  
}

Dictionary<Key, object>
  

Я думаю, что это помогло бы?

Комментарии:

1. Да, это, безусловно, самое простое 🙂

2. Как два ключа будут сравниваться по словарю? Использует ли он == или IComparable?

3. Это тип значения, поэтому он просто выполнит == . Я не уверен насчет 32-разрядных систем, но в 64-разрядной системе, я думаю, было бы просто сравнить две ячейки памяти, поскольку перечисление и int поместятся в 64-разрядную версию, это дешевая операция. На 32-разрядной версии, вероятно, (?) потребовалось бы еще несколько инструкций?

4. Преимуществом этого решения является производительность и простота. Это дешево для сравнения. Но вы не можете управлять сортировкой, например

5. Хм, когда я думаю об этом, вы можете указать порядок, в котором поля хранятся внутри. Это должно позволить вам определить внутреннюю релевантность между перечислением и int

Ответ №2:

Если вы собираетесь поместить это в словарь, то вам нужно будет убедиться, что вы реализуете значимый .Equals и .GetHashCode , иначе словарь будет вести себя некорректно.

Я бы начал с чего-то подобного следующему для базового составного ключа, а затем внедрил пользовательский IComparer для получения нужного вам порядка сортировки.

 public class MyKey
{
    private readonly SomeEnum enumeration;
    private readonly int number;

    public MyKey(SomeEnum enumeration, int number)
    {
        this.enumeration = enumeration;
        this.number = number;
    }

    public int Number
    {
        get { return number; }
    }

    public SomeEnum Enumeration
    {
        get { return enumeration; }
    }

    public override int GetHashCode()
    {
        int hash = 23 * 37   this.enumeration.GetHashCode();
        hash = hash * 37   this.number.GetHashCode();

        return hash;
    }

    public override bool Equals(object obj)
    {
        var supplied = obj as MyKey;
        if (supplied == null)
        {
            return false;
        }

        if (supplied.enumeration != this.enumeration)
        {
            return false;
        }

        if (supplied.number != this.number)
        {
            return false;
        }

        return true;
    }
}
  

Комментарии:

1. 1 за упоминание . Equals и GetHashCode попались. Это хорошая причина использовать тип Tuple вместо того, чтобы использовать свой собственный простой ключ.

2. Я полагаю, что структуры имеют переопределения GetHashCode и Equals по умолчанию, которые дадут правильные результаты, хотя GetHashCode по умолчанию просто хэширует первый элемент в структуре. Производительность будет хорошей, если большинство структур отличаются первым элементом, и очень плохой, если большинство структур имеют один и тот же первый элемент.

Ответ №3:

Если вы используете C # 4.0, вы могли бы использовать класс Tuple.

 var key = Tuple.Create(SomeEnum.Value1, 3);
  

Комментарии:

1. Да, но тогда он неправильно напечатан — т. Е. там нет описания того, из чего состоит ключ. Но да, это возможно

2. Он строго типизирован — ключ имеет тип Tuple<SomeEnum, int> .

3. Итак, ваш словарь может быть определен как Dictionary<Tuple<SomeEnum, int>, object>

4. Да, он строго типизирован, но не является описательным. Если вы позже заполучите этот ключ, знаете ли вы, что это за целое число на самом деле? Это моя точка зрения. Это может быть, а может и не быть проблемой. Это зависит 🙂

5. Он «строго типизирован», но не «красиво типизирован». С помощью структуры (или анонимного типа) вы можете получить доступ к элементам с помощью x.nameyousspecify. С помощью кортежа вы получаете доступ к x.Item1, который далеко не так информативен.