private SortedList<TargetKey, Target> NewTarget = new SortedList<TargetKey, Target>();
Структура значений:
public struct Target
{
public String Code;
public String Name;
public String ItemCode;
public String ItemName;
public String DirecionCode;
public String DirecionName;
public String ParentCode;
public Boolean isNP; //???public Byte Level;
public DateTime DateBegin;
public DateTime DateEnd;
public void PrintToExcelSheet(ExcelWorksheet worksheet, int Row)
{
worksheet.Cells[Row, 1].Value = Row - 1;
worksheet.Cells[Row, 2].Value = ItemCode;
worksheet.Cells[Row, 3].Value = ItemName;
worksheet.Cells[Row, 4].Value = DirecionCode;
worksheet.Cells[Row, 5].Value = DirecionName;
worksheet.Cells[Row, 6].Value = Level;
worksheet.Cells[Row, 7].Value = DateEnd.ToString("dd.MM.yyyy");
return;
}
}
Класс ключей:
public class TargetKey : IEquatable<TargetKey>, IComparable<TargetKey>
{
private String _Code;
private String _Name;
private byte _Level;
public TargetKey()
{
this._Code = "";
this._Name = "";
this._Level = 0;
}
public TargetKey(String Code, String Name, byte Level)
{
this._Code = Code;
this._Name = Name;
this._Level = Level;
}
public void Set(String Code, String Name, byte Level)
{
this._Code = Code;
this._Name = Name;
this._Level = Level;
}
public String Code
{
get { return _Code; }
set { _Code = value; }
}
public String Name
{
get { return _Name; }
set { _Name = value; }
}
public byte Level
{
get { return _Level; }
set { _Level = value; }
}
public override bool Equals(object obj)
{
if (obj == null) return false;
TargetKey objAsTargetKey = obj as TargetKey;
if (objAsTargetKey == null) return false;
else return Equals(objAsTargetKey);
}
public bool Equals(TargetKey x)
{
return this._Code.Equals(x._Code) && this._Name.Equals(x._Name) && this._Level.Equals(x._Level);
}
public override int GetHashCode()
{
return this._Code.GetHashCode() + this._Name.GetHashCode() + this._Level.GetHashCode();
}
public int CompareTo(TargetKey compareTargetKey)
{
if (this == compareTargetKey) return 0;
if (this < compareTargetKey) return -1;
return 1;
}
public static bool operator ==(TargetKey a, TargetKey b)
{
if (a is null && b is null) return true;
if (a is null || b is null) return false;
if (a._Code == b._Code && a._Name == b._Name && a._Level == b._Level) return true;
else return false;
}
public static bool operator !=(TargetKey a, TargetKey b) => !(a == b);
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;
}
public static bool operator <=(TargetKey a, TargetKey b)
{
return (a < b) || (a == b);
}
public static bool operator >=(TargetKey a, TargetKey b)
{
return (a > b) || (a == b);
}
}
Список заполнен. Беру из него коллекцию ключей, прохожу по ней и смотрю значения, соответствующие ключу.
Вылетает исключение: System.Collections.Generic.KeyNotFoundException: "Данный ключ отсутствует в словаре."
Как в словаре может отсутствовать ключ, взятый из самого этого ключа?
Единственное, что могу предположить — некорректная перегрузка операторов сравнения и/или компаратора, но не понимаю, где именно.
Помогите найти проблемы.
ЗЫ. Если что, это не является моей профессиональной деятельностью.
Re: Ключ, взятый из коллекции ключей, отсутствует в словаре
т.к. пары (в SortedList) уже будут упорядочены по ключу.
ВИ>Вылетает исключение: System.Collections.Generic.KeyNotFoundException: "Данный ключ отсутствует в словаре." ВИ>Как в словаре может отсутствовать ключ, взятый из самого этого ключа? ВИ>Единственное, что могу предположить — некорректная перегрузка операторов сравнения и/или компаратора, но не понимаю, где именно.
Чтобы это понять, надо узнать значение ключа (tT) на котором это происходит.
Re: Ключ, взятый из коллекции ключей, отсутствует в словаре
Здравствуйте, Воронин Иван, Вы писали:
ВИ>Единственное, что могу предположить — некорректная перегрузка операторов сравнения и/или компаратора, но не понимаю, где именно.
Бинго!
ВИ>Помогите найти проблемы.
Здесь используется что-то вроде лексикографического порядка. В нем важно, что нет смысла сравнивать очередную пару полей, если предыдущая пара не совпала полностью.
Ну т.е. результат сравнения "abc" и "bcd" уже очевиден по сравнению первых символов.
Re: Ключ, взятый из коллекции ключей, отсутствует в словаре
Здравствуйте, Воронин Иван, Вы писали:
ВИ>Единственное, что могу предположить — некорректная перегрузка операторов сравнения и/или компаратора, но не понимаю, где именно.
Нужно убедиться в том, что a.CompareTo(b) и b.CompareTo(a) возвращают не противоречащие друг другу результаты.
Если смотрим оператор <, то при a.Code < b.Code возвращается сразу a < b.
Должны тогда и при a.Code > b.Code сразу возвращать, что a > b, но там идёт переход к сравнению Name и уже от него зависит кто меньше.
В итоге одновременно можно получить утверждения "a < b" и "a > b" и работа с таким типом будет непредсказуема.
В идеале и оператор > нужно привести в соответствие оператору <, а то там такая же история, но именно на коллекцию оно вроде бы не должно влиять в данном случае.
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
Здравствуйте, xpalex, Вы писали:
X>Вообще референсным способом добавления операций сравнения является реализция кода сравнения в единственном месте (в методе CompareTo), а реализации операторов должны переиспользовать CompareTo. В вашем примере сделано наоборот (две реализации и обе некорректных) и из-за этого появилось нарушение контракта для реализации IComparable