Ошибка в BLToolkit.ComponentModel.BindingListImpl
От: Rollback Россия  
Дата: 17.03.08 04:16
Оценка:
Мне кажется в BLToolkit.ComponentModel.BindingListImpl.GetItemSortedPosition ошибка. Публикую правильный, на мой взгляд, код.

        public int GetItemSortedPosition(int index, object sender)
        {
            IComparer comparer = GetSortComparer();

            if (comparer == null)
                return index;

            int result     = index > 0 ? comparer.Compare(_list[index - 1], sender) : -1;
            int nextResult = index < _list.Count - 1 ? comparer.Compare(_list[index + 1], sender) : 1;

            if (result > 0 || nextResult < 0)
            {
                for (int i = 0; i < _list.Count; i++)
                {
                    if (i == index)
                        continue;

                    result = comparer.Compare(_list[i], sender);

                    #region alex
                    
                    if (result > 0)
                        return i - 1;

                    /*if (result > 0)
                    {
                        if (index == i - 1)
                            return index;
                                
                        return i;
                    }*/

                    #endregion                    
                }

                return _list.Count - 1;
            }

            return index;
        }
Re: Ошибка в BLToolkit.ComponentModel.BindingListImpl
От: Rollback Россия  
Дата: 17.03.08 04:56
Оценка:
Вернее даже вот так

if (result > 0)
{
   if (i > 0)
      return i - 1;
   return i;
}
Re[2]: Ошибка в BLToolkit.ComponentModel.BindingListImpl
От: Rollback Россия  
Дата: 17.03.08 05:55
Оценка: 2 (1)
В общем, я пока остановился на такой реализации:
        public int GetItemSortedPosition(int index, object sender)
        {
            IComparer comparer = GetSortComparer();
            if (comparer == null)
                return index;
            if (_list.Count > 1)
                for (int i = 0; i < _list.Count; i++)
                    if (i != index)
                    {
                        if (comparer.Compare(_list[i], sender) > 0)
                        {
                            if (i > 0)
                                return i - 1;
                            return i;
                        }
                    }
            return _list.Count - 1;
        }


ЗЫ: я не сказал в чём ошибка

Имеем отсортированный список. Значения поля, по которому идёт сортировка:
1, 1, 2, 3

Если производим замену объекта с индексом 0, на объект со значением поля 2, то он встанет по индексу 3, а должен по индексу 2.

Получается:
1, 2, 3, 2

А должно быть:
1, 2, 2, 3

Т. е. возвращается индекс на единицу больше правильного.

Замену объекта произвожу командой myBindingSource[0] = myObj;
Re[3]: Ошибка в BLToolkit.ComponentModel.BindingListImpl
От: Rollback Россия  
Дата: 17.03.08 06:15
Оценка:
Ещё один момент не учёл. Теперь вроде всё правильно.
        public int GetItemSortedPosition(int index, object sender)
        {
            IComparer comparer = GetSortComparer();
            if (comparer == null)
                return index;
            if (_list.Count > 1)
                for (int i = 0; i < _list.Count; i++)
                    if (i != index && comparer.Compare(_list[i], sender) > 0)
                    {
                        if (i > 0 && i > index)
                            return i - 1;
                        return i;
                    }
            return _list.Count - 1;
        }
Re[4]: Ошибка в BLToolkit.ComponentModel.BindingListImpl
От: Rollback Россия  
Дата: 17.03.08 07:34
Оценка:
Ну и ещё одно условие, и всё становится на свои места.

        public int GetItemSortedPosition(int index, object sender)
        {
            IComparer comparer = GetSortComparer();
            if (comparer == null)
                return index;
            if (_list.Count > 1)
            {
                if (index < _list.Count - 1 && comparer.Compare(_list[index + 1], sender) == 0)
                    return index;
                for (int i = 0; i < _list.Count; i++)
                    if (i != index && comparer.Compare(_list[i], sender) > 0)
                    {
                        if (i > index)
                            return i - 1;
                        return i;
                    }
            }
            return _list.Count - 1;
        }


ЗЫ: Можно конечно при Remove / Insert коррекцию индекса производить...
Завязывай сам с собой разговаривать.l
От: Блудов Павел Россия  
Дата: 17.03.08 07:45
Оценка:
Здравствуйте, Rollback!

Напишите, пожалуйста, нормальный unit test, который наглядно проиллюстрирует что же именно не так работает.
А то может это и не баги вовсе а фичи такие.
... << RSDN@Home 1.2.0 alpha 2 rev. 872>>
Re: Завязывай сам с собой разговаривать.l
От: Rollback Россия  
Дата: 17.03.08 08:25
Оценка:
Здравствуйте, Блудов Павел, Вы писали:

БП>Здравствуйте, Rollback!


БП>Напишите, пожалуйста, нормальный unit test, который наглядно проиллюстрирует что же именно не так работает.

БП>А то может это и не баги вовсе а фичи такие.

Повторюсь:

Имеем отсортированный список. Значения поля, по которому идёт сортировка:
1, 1, 2, 3

Если производим замену объекта с индексом 0, на объект со значением поля 2, то он встанет по индексу 3, а должен по индексу 2.

Получается:
1, 2, 3, 2

А должно быть:
1, 2, 2, 3

Т. е. возвращается индекс на единицу больше правильного.

Замену объекта произвожу командой myBindingSource[0] = myObj;

Если будет время — напишу тест.
Re[2]: Завязывай сам с собой разговаривать.l
От: Rollback Россия  
Дата: 17.03.08 08:44
Оценка: 98 (2)
Вот тест:
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            EditableList<MyObj> dataList = new EditableList<MyObj>();
            dataList.Add(new MyObj(1));
            dataList.Add(new MyObj(1));
            dataList.Add(new MyObj(1));
            dataList.Add(new MyObj(2));
            dataList.Add(new MyObj(2));
            dataList.Add(new MyObj(3));
            dataList.Add(new MyObj(3));
            dataList.Add(new MyObj(4));
            dataList.Add(new MyObj(4));
            dataList.Add(new MyObj(4));
            bindingSource1.DataSource = dataList;
            bindingSource1.Sort = "A";
        }

        private void button1_Click(object sender, EventArgs e)
        {
            bindingSource1[0] = new MyObj(3);
        }
    }

    public class MyObj
    {
        public MyObj(int a)
        {
            A = a;
        }

        private int? m_A;
        public int? A
        {
            get { return m_A; }
            set { m_A = value; }
        }
    }
fixed
От: Блудов Павел Россия  
Дата: 26.03.08 07:14
Оценка:
Здравствуйте, Rollback!
Будем считать этот вариант окончательным и внесём в репозиторий.
... << RSDN@Home 1.2.0 alpha 2 rev. 872>>
Re[3]: Ошибка в BLToolkit.ComponentModel.BindingListImpl
От: BorisKV  
Дата: 28.03.08 03:22
Оценка: 98 (2)
Здравствуйте, Rollback, Вы писали:

R>Имеем отсортированный список. Значения поля, по которому идёт сортировка:

R>1, 1, 2, 3

R>Если производим замену объекта с индексом 0, на объект со значением поля 2, то он встанет по индексу 3, а должен по индексу 2.


R>Получается:

R>1, 2, 3, 2

R>А должно быть:

R>1, 2, 2, 3

R>Т. е. возвращается индекс на единицу больше правильного.


R>Замену объекта произвожу командой myBindingSource[0] = myObj;


Спасибо большое, что вы нашли и указали на ошибку, и предложишили решение. Однако, непонятно зачем было переписывать весь метод, не вникая зачем в нем была дополнительная логика (наверное это наша русская особенность ).

BLToolkit используется в Trading приложении в одном весьма не маленьком банке, и после забора нового кода, я долго пытался понять, почему приложение вдруг перестало быть отзывчивым, UI замерирал на мгновения и тому подобные симптомы, и это в тестовом окружении (значительно меньше объемы данных). А оказалось... Если вы посмотрите на начальный код, а именно:
int result     = index > 0 ? comparer.Compare(_list[index - 1], sender) : -1;
int nextResult = index < _list.Count - 1 ? comparer.Compare(_list[index + 1], sender) : 1;

if (result > 0 || nextResult < 0)

То вы наверное заметите, что прежде, чем искать позицию, код проверял, надо ли вообще искать новое место, может объект надо оставить на месте. Представте себе список не из 10 объектов, а из нескольких тысяч по меньшей мере. Затем представте, что приложение отображает trades и их изменения в Real-Time, т.е. обноволений много и они происходят часто, иногда до 20 за секунду, в результате OnPropertyChanged (место откуда чаще всего дергается метод) вызвается несчислимое число раз. Далее представте, что поле по которому происходит сортировка — это не простое Property, а довольно сложное комплексное поле. В результате представте сколько лишних сравнений сделает эта функция, если к примеру сортировка идет по одному полю, а меняется другое поле.

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

На самом деле починить проблему можно было очень просто.
Вместо:
if (index == i - 1)
    return index;

Надо было всего лишь, как вы правильно заметили:
if (i > index)
  return i - 1;


Так, что с вашего позволения я все-таки верну на место эту логику за исключением вышеописанного изменения.

Борис.
... << RSDN@Home 1.2.0 alpha rev. 693>>
Re[4]: Ошибка в BLToolkit.ComponentModel.BindingListImpl
От: IT Россия linq2db.com
Дата: 28.03.08 17:30
Оценка: :))
Здравствуйте, BorisKV, Вы писали:

BKV>BLToolkit используется в Trading приложении в одном весьма не маленьком банке


Mission-critical application? Это не BLToolkit случайно фондовый рынок обвалил?
Если нам не помогут, то мы тоже никого не пощадим.
Re: Ошибка в BLToolkit.ComponentModel.BindingListImpl
От: Rollback Россия  
Дата: 01.04.08 04:34
Оценка:
Всем спасибо за отзывы.

Рад, что моё сообщение оказалось полезным.
Re[4]: Ошибка в BLToolkit.ComponentModel.BindingListImpl
От: Rollback Россия  
Дата: 01.04.08 06:46
Оценка:
Здравствуйте, BorisKV, Вы писали:

BKV>Так, что с вашего позволения я все-таки верну на место эту логику за исключением вышеописанного изменения.



Да, вы правы. Если не возражаете, я пожалуй тоже остановлюсь на Вашем решении.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.