#c_sharp #массивы #коллекции
Здравствуйте. Имеется коллекция _data (Dictionary), где ключем выступает массив типа int[], а значением является условный тип. private static Dictionary_data = new Dictionary (); Я добавил в коллекцию какие то данные, например так: _data.Add(new int[2] { 1, 2 }, new SomeType(p1, p2)); Но при получении значений возникла проблема, если написать: _data.ContainsKey(new int[2] { 1, 2 }); То всегда будет false. Я так понял потому что инициализирую все время новый массив, а не даю ссылку на него. Сам вопрос заключается в том, как лучше и проще всего в данном случае оперировать с набором данных как ключем. Я только изучаю этот прекрасный язык, недавно почитав о структурах, понял что может быть их лучше использовать в данном случае, но все таки хотел узнать у опытных программистов.
Ответы
Ответ 1
Дело в том, что Dictionaryиспользует 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 в первую очередь сравнивает хеш-коды, а потом уже пытается сравнить объекты. Для вашей задачи нужно создать класс, который будет содержать внутри себя массив, переопределить в нем GetHashCode и метод Equals. А еще лучше реализовать интерфейс IEquatable. Как правильно реализовать GetHashCode - отдельный вопрос. Можно например "скопипастить" GetHashCode из класса System.String, который уж точно реализован отлично: class MyArray : IEquatable { 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 Ответ 2
Вы правы, получить данные не удается, потому что ваш ключ ссылочного типа, и вы всегда создаете новый объект, вместо того чтобы использовать ссылку на оригинальный. В тех случаях, когда вы нигде не храните ссылки на оригинальные объекты, удобнее использовать в качестве ключей значимые типы или строки. Например - вот так: private string GetKey(params int[] values) { return string.Join("_", values); } _data.Add(GetKey(1, 2), new SomeType(p1, p2)); В таком случае вы в любое сможете получить ключ, нигде не храня его. Использовать ссылки на объекты в качестве ключей удобно только тогда, когда эти объекты у вас где-то еще "работают", и время от времени возникает необходимость получить информацию, имея на руках ссылку на такой объект. Судя по коду, ваша программа устроена не так.
Комментариев нет:
Отправить комментарий