Подтвердить что ты не робот

CollectionChanged и IList of Items - почему трудности

Я изучаю тему, почему ObservableCollection/ListCollectionView/CollectionView вызывает NotSuportedException при вызове CollectionChanged с параметром IList.

//Throws an exception
private void collectionChanged_Removed(IList items)
{
    if (CollectionChanged != null)
        CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, items));
}

Я нашел несколько веб-страниц, рассказывая об этой теме, и они предлагают либо использовать способ Reset для принудительной полной перерисовки пользовательского интерфейса, либо просто вызвать для каждого элемента CollectionChanged или несколько более творческий способ: http://geekswithblogs.net/NewThingsILearned/archive/2008/01/16/listcollectionviewcollectionview-doesnt-support-notifycollectionchanged-with-multiple-items.aspx

Я просто не могу найти ПОЧЕМУ? Для меня нет никакого смысла, почему это будет так.

Есть ли вероятность, что эта недостающая функция, с которой мы все сталкиваемся в какой-то момент нашего цикла разработки, поскольку метод Add просто имеет большую часть накладных расходов, когда вы хотите быстро добавить несколько элементов, будет выполняться в любое время (.Net 5, С# 6...).

Edit:

В моем конкретном случае я написал свой собственный класс:

public class ObservableList<T> : IList<T>, IList, IEnumerable<T>,
    INotifyCollectionChanged
{
    public event NotifyCollectionChangedEventHandler CollectionChanged;
    //other stuff...
}

И все равно выбрасывает указанное NotSupportedException.

4b9b3361

Ответ 1

Вдохновленный ответом VirtualBlackFox, я посмотрел под капотом классов CollectionView в ILSpy. Похоже, что основная причина почему отсутствие поддержки операций с диапазоном заключается в том, что внутри CollectionView использует журнал изменений для централизованного управления ожидающими изменения всех видов и отправки сообщений по каждому элементу.

По своей сути CollectionView может хранить 1000 записей, используемых одновременно с несколькими элементами управления пользовательского интерфейса, представляющими его базовые данные. Таким образом, добавление или удаление записей должно выполняться на атомной основе, чтобы поддерживать целостность элементов управления пользовательского интерфейса, которые обращаются к информации о просмотре. Вы не можете синхронизировать инкрементные изменения с несколькими подписчиками пользовательского интерфейса, используя события массового изменения, не передавая функции группировки, сортировки и фильтрации CollectionView на элементы управления пользовательского интерфейса, которые его используют.

CollectionView также происходит от System.Windows.Threading.Dispatcher, поэтому проблема может быть связана с тем, как она управляет рабочими элементами в ней. Путь вызовов включает защищенный метод ProcessCollectionChanged, который специально обрабатывает отдельные изменения в потоке пользовательского интерфейса. Таким образом, диапазоны обновления могут влиять на всю модель потоковой передачи, которую он использует для взаимодействия с элементами пользовательского интерфейса, которые ее используют.

Я полностью согласен с тем, что наличие пользователей CollectionView pass в IList до NotifyCollectionChangedEventArgs глупо. Он специально отвергает все с длиной!= 1 и жесткими кодами для args.NewItems[0] внутри.

Ответ 2

Как сказал @nmclean в комментариях, проблема не в сборке, излучающей CollectionChanged, а на принимающей стороне.

Если вы посмотрите на код ListCollectionView (например, используя DotPeek или в новых версиях visual studio, вы можете получить доступ к исходному источнику код) вы заметите, что каждый раз, когда вложенная коллекция меняет вызов, вызывается метод ValidateCollectionChangedEventArgs, который бросает, когда изменяется более одного элемента

private void ValidateCollectionChangedEventArgs(NotifyCollectionChangedEventArgs e)
{
  switch (e.Action)
  {
    case NotifyCollectionChangedAction.Add:
      if (e.NewItems.Count == 1)
        break;
      else
        throw new NotSupportedException(System.Windows.SR.Get("RangeActionsNotSupported"));
...

Остальная часть класса и базовый класс CollectionView уже являются большими животными (2710 и 2027 строк в источнике, опубликованном в исходном исходном коде), поэтому Microsoft, возможно, захотела избежать поддержки сложного случая, что коллекция, которую они рекомендую не создавать в любом случае.

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

Я не нашел предложений, связанных с добавлением поддержки событий диапазона в Microsoft Connect, но вы должны представить свои собственные, если это важно для вас.

Ответ 3

Я предполагаю, что это в основном по причинам производительности. Я также был шокирован, когда увидел, что CollectionView также не принимает значение -1 для NewStartingIndex или OldStartingIndex. Поэтому в основном CollectionView всегда хочет отображать элементы из своих индексов. Тем не менее, это не требует, чтобы это сопоставление было точным (что странно с моей точки зрения), допускается, что NewStartingIndex меньше, чем правильный индекс (если он не равен -1).

Я думаю, что корень проблемы заключается в том, что большие части внутри Microsoft по-прежнему считают, что список является единственным и единственным способом реализации коллекции, что, конечно, просто неверно. Здесь создатели NotifyCollectionChangedEventArgs думали об альтернативах (таких как связанные списки или коллекции хеширования), но ребята из UI не сделали этого. Или, по крайней мере, они не хотели поддерживать эти коллекции, поскольку они появляются довольно редко.

Ответ 4

Временное решение бесполезно. Это только скрывает проблемы. Решение может заключаться в создании событий, в которых вы предоставляете наблюдателям полный список. Таким образом, Microsoft не должна будет внедрять эффективные обработчики диапазона для каждого типа наблюдателя.