Массив как ключ в коллекции C#

338
17 октября 2017, 00:17

Здравствуйте.

Имеется коллекция _data (Dictionary), где ключем выступает массив типа int[], а значением является условный тип.

private static Dictionary<int[], SomeType> _data = new Dictionary<int[], SomeType>();

Я добавил в коллекцию какие то данные, например так:

_data.Add(new int[2] { 1, 2 }, new SomeType(p1, p2));

Но при получении значений возникла проблема, если написать:

_data.ContainsKey(new int[2] { 1, 2 });

То всегда будет false. Я так понял потому что инициализирую все время новый массив, а не даю ссылку на него. Сам вопрос заключается в том, как лучше и проще всего в данном случае оперировать с набором данных как ключем. Я только изучаю этот прекрасный язык, недавно почитав о структурах, понял что может быть их лучше использовать в данном случае, но все таки хотел узнать у опытных программистов.

Answer 1

Вы правы, получить данные не удается, потому что ваш ключ ссылочного типа, и вы всегда создаете новый объект, вместо того чтобы использовать ссылку на оригинальный.

В тех случаях, когда вы нигде не храните ссылки на оригинальные объекты, удобнее использовать в качестве ключей значимые типы или строки. Например - вот так:

private string GetKey(params int[] values) {
  return string.Join("_", values);
}
_data.Add(GetKey(1, 2), new SomeType(p1, p2));

В таком случае вы в любое сможете получить ключ, нигде не храня его.

Использовать ссылки на объекты в качестве ключей удобно только тогда, когда эти объекты у вас где-то еще "работают", и время от времени возникает необходимость получить информацию, имея на руках ссылку на такой объект. Судя по коду, ваша программа устроена не так.

Answer 2

Дело в том, что Dictionary<TKey, TValue> использует GetHashCode. Класс Array не переопределяет этот метод, а значит используется реализация класса System.Object. Под капотом вызывается нативный метод CLR ObjectNative::GetHashCode, который делает что-то, чего никто не знает. Но факт в том, что одинаковые объекты имеют разный хеш код, если они находятся по разным адресам. Вот пример:

int[] x1 = new[] { 1, 2 };
int[] x2 = new[] { 1, 2 };
Console.WriteLine(x1.GetHashCode());
Console.WriteLine(x2.GetHashCode());

Выводит:

46104728
12289376

Dictionary<TKey, TValue> в первую очередь сравнивает хеш-коды, а потом уже пытается сравнить объекты.

Для вашей задачи нужно создать класс, который будет содержать внутри себя массив, переопределить в нем GetHashCode и метод Equals. А еще лучше реализовать интерфейс IEquatable. Как правильно реализовать GetHashCode - отдельный вопрос. Можно например "скопипастить" GetHashCode из класса System.String, который уж точно реализован отлично:

class MyArray : IEquatable<MyArray>
{
    private int[] _array;
    public MyArray(int[] array)
    {
        this._array = array;
    }
    public override int GetHashCode()
    {
        int num = 5381;
        int num2 = num;
        for (int i = 0; i < _array.Length; i += 2)
        {
            num = ((num << 5) + num ^ _array[i]);
            if (i + 1 == _array.Length)
                break;
            num2 = ((num2 << 5) + num2 ^ _array[i + 1]);
        }
        return num + num2 * 1566083941;
    }
    // Equals и др.
}

Теперь вам вместо ключа int[] нужно использовать экземпляры MyArray.

int[] x1 = new[] { 1, 2 };
int[] x2 = new[] { 1, 2 };
var a1 = new MyArray(x1);
var a2 = new MyArray(x2);
Console.WriteLine(a1.GetHashCode());
Console.WriteLine(a2.GetHashCode());

Такой код теперь покажет одинаковые хеш-коды:

-1776415353
-1776415353
READ ALSO
Программное управление Tor в Windows

Программное управление Tor в Windows

Ищу способ сменить выходную ноду тора программно из c#Ну и было бы здорово узнать как указать определенные страны выходной ноды программно,...

221
jquery события выделения

jquery события выделения

У меня есть тег <textarea></textarea> в который с ввожу текст

228
ReferenceError: link is not defined (nodejs)

ReferenceError: link is not defined (nodejs)

ЗдравствуйтеЯ нуб

360
Javascript: перемещение по div, как по google maps

Javascript: перемещение по div, как по google maps

Есть div, который намного больше размера окнаИщу такую библиотеку или способ, чтобы по этому элементу можно было перемещаться как в гугл картах...

287