Информация об изменениях

Сообщение Re: Ключ, взятый из коллекции ключей, отсутствует в словаре от 31.03.2022 8:28

Изменено 31.03.2022 8:41 xpalex

Re: Ключ, взятый из коллекции ключей, отсутствует в словаре
Здравствуйте, Воронин Иван, Вы писали:

Проблема тут:
ВИ>
ВИ>        public static bool operator <(TargetKey a, TargetKey b)
ВИ>        {
ВИ>            if (a is null || b is null) return false;
ВИ>            if (a._Code.CompareTo(b._Code) < 0) return true;
ВИ>            if (a._Name.CompareTo(b._Name) < 0) return true;
ВИ>            if (a._Level.CompareTo(b._Level) < 0) return true;
ВИ>            return false;
ВИ>        }

ВИ>        public static bool operator >(TargetKey a, TargetKey b)
ВИ>        {
ВИ>            if (a is null || b is null) return false;
ВИ>            if (a._Code.CompareTo(b._Code) > 0) return true;
ВИ>            if (a._Name.CompareTo(b._Name) > 0) return true;
ВИ>            if (a._Level.CompareTo(b._Level) > 0) return true;
ВИ>            else return false;
ВИ>        }

ВИ>


Реализация SortedList использует массив для хранения элементов и свойство SortedList.Item[] использует бинарный поиск для получения индекса элемента в массиве.
gecnm

Соответственно, что бы дойти до существующего ключа бинарным поиском, нужна корректная реализация интерфейса System.IComparable (метод
public int CompareTo(TargetKey compareTargetKey)
) для типа ключа.

Пусть есть ключи k1, k2, где k1 = TargetKey("3", "a", 1) и k2 = TargetKey("2", "b", 1))
k2.Compare(k1) == -1, т, к. оператор "<" вернет true в строке
if (a._Code.CompareTo(b._Code) < 0) return true;

и k1.Compare(k2) == -1, т, к. оператор "<" вернет true в строке
if (a._Name.CompareTo(b._Name) > 0) return true;


при добавлении в список первого ключа — он попадет в первую позицию списка (индекс 0),
при добавлении второго , т.к. k2 < k1 — k2 попадет в первую, а k1 переместится во вторую (индекс 1).

про попытыке найти элемент по ключу k1, будет взят ключ для сравнения из середины списка (индекс = floor(list.size/2) — 1 == 0), т.е. k2.
т.к. k1.CompareTo(k2) == -1, то алгоритм поиска попытается искать слева от индекса 0. Что и приводит к исключению.


Вообще референсным способом добавления операций сравнения является реализция кода сравнения в единственном месте (в методе CompareTo), а реализации операторов должны переиспользовать CompareTo. В вашем примере сделано наоборот (две реализации и обе некорректных) и из-за этого появилось нарушение контракта для реализации IComparable
Re: Ключ, взятый из коллекции ключей, отсутствует в словаре
Здравствуйте, Воронин Иван, Вы писали:

Проблема тут:
ВИ>
ВИ>        public static bool operator <(TargetKey a, TargetKey b)
ВИ>        {
ВИ>            if (a is null || b is null) return false;
ВИ>            if (a._Code.CompareTo(b._Code) < 0) return true;
ВИ>            if (a._Name.CompareTo(b._Name) < 0) return true;
ВИ>            if (a._Level.CompareTo(b._Level) < 0) return true;
ВИ>            return false;
ВИ>        }

ВИ>        public static bool operator >(TargetKey a, TargetKey b)
ВИ>        {
ВИ>            if (a is null || b is null) return false;
ВИ>            if (a._Code.CompareTo(b._Code) > 0) return true;
ВИ>            if (a._Name.CompareTo(b._Name) > 0) return true;
ВИ>            if (a._Level.CompareTo(b._Level) > 0) return true;
ВИ>            else return false;
ВИ>        }

ВИ>


Реализация SortedList использует массив для хранения элементов и свойство SortedList.Item[] использует бинарный поиск для получения индекса элемента в массиве.

Соответственно, что бы дойти до существующего ключа бинарным поиском, нужна корректная реализация интерфейса System.IComparable (метод
public int CompareTo(TargetKey compareTargetKey)
) для типа ключа.

Пусть есть ключи k1, k2, где k1 = TargetKey("3", "a", 1) и k2 = TargetKey("2", "b", 1))
k2.Compare(k1) == -1, т, к. оператор "<" вернет true в строке
if (a._Code.CompareTo(b._Code) < 0) return true;

и k1.Compare(k2) == -1, т, к. оператор "<" вернет true в строке
if (a._Name.CompareTo(b._Name) < 0) return true;


при добавлении в список первого ключа — он попадет в первую позицию списка (индекс 0),
при добавлении второго , т.к. k2 < k1 — k2 попадет в первую, а k1 переместится во вторую (индекс 1).

про попытыке найти элемент по ключу k1, будет взят ключ для сравнения из середины списка (индекс = floor(list.size/2) — 1 == 0), т.е. k2.
т.к. k1.CompareTo(k2) == -1, то алгоритм поиска попытается искать слева от индекса 0. Что и приводит к исключению.


Вообще референсным способом добавления операций сравнения является реализция кода сравнения в единственном месте (в методе CompareTo), а реализации операторов должны переиспользовать CompareTo. В вашем примере сделано наоборот (две реализации и обе некорректных) и из-за этого появилось нарушение контракта для реализации IComparable