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

Наблюдаемый стек и очередь

Я ищу реализацию INotifyCollectionChanged Stack и Queue. Я мог бы свернуть самостоятельно, но я не хочу изобретать велосипед.

4b9b3361

Ответ 1

С помощью стеков и очередей (почти по определению) у вас есть доступ только к вершине стека или главы очереди. Это отличает их от List. (и поэтому, почему вы его не нашли)

Чтобы ответить, хотя вы можете написать свой собственный, я бы сделал это, получив ObservableCollection, а затем в случае стека, реализующего Push как Insert при смещении 0 (и pop как возвращающий индекс 0 то RemoveAt индекс 0); или с помощью очереди вы можете просто Add до конца списка до Enqueue, а захватить и удалить первый элемент, как и стек, для Dequeue. Операции Insert, Add и RemoveAt будут вызываться в базовом ObservableCollection и поэтому вызывают событие CollectionChanged, которое должно быть запущено.


Вы также можете сказать, что просто хотите связать или получить уведомление, когда один элемент, который должен иметь доступ к изменениям. Вы снова создадите свой собственный класс, полученный из Stack или Queue, и запустите событие CollectionChanged вручную, когда:

  • Что-то нажимается или выталкивается из стека
  • Что-то выгружено из очереди
  • Что-то поставлено в очередь в очереди, когда очередь была ранее пустой

Ответ 2

Я сталкиваюсь с той же проблемой и хочу поделиться своим решением с другими. Надеюсь, это кому-то поможет.

public class ObservableStack<T> : Stack<T>, INotifyCollectionChanged, INotifyPropertyChanged
{
    public ObservableStack()
    {
    }

    public ObservableStack(IEnumerable<T> collection)
    {
        foreach (var item in collection)
            base.Push(item);
    }

    public ObservableStack(List<T> list)
    {
        foreach (var item in list)
            base.Push(item);
    }


    public new virtual void Clear()
    {
        base.Clear();
        this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
    }

    public new virtual T Pop()
    {
        var item = base.Pop();
        this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item));
        return item;
    }

    public new virtual void Push(T item)
    {
        base.Push(item);
        this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item));
    }


    public virtual event NotifyCollectionChangedEventHandler CollectionChanged;


    protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        this.RaiseCollectionChanged(e);
    }

    protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        this.RaisePropertyChanged(e);
    }


    protected virtual event PropertyChangedEventHandler PropertyChanged;


    private void RaiseCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        if (this.CollectionChanged != null)
            this.CollectionChanged(this, e);
    }

    private void RaisePropertyChanged(PropertyChangedEventArgs e)
    {
        if (this.PropertyChanged != null)
            this.PropertyChanged(this, e);
    }


    event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged
    {
        add { this.PropertyChanged += value; }
        remove { this.PropertyChanged -= value; }
    }
}

Ответ 3

Очень похоже на приведенный выше класс, за некоторыми исключениями:

  1. Публикация реквизита изменена для изменений коллекции для Count
  2. Переопределить TrimExcess() b/c, который может повлиять на количество
  3. Обнародовал события, поэтому мне не нужно приводить к интерфейсу
  4. При необходимости передает индекс в коллекцию
    public class ObservableStack : Stack, INotifyPropertyChanged, INotifyCollectionChanged
    {
      public ObservableStack(IEnumerable collection) : base(collection) {}
      public ObservableStack() { } 

      public event PropertyChangedEventHandler PropertyChanged = delegate { };
      public event NotifyCollectionChangedEventHandler CollectionChanged = delegate { };

      protected virtual void OnCollectionChanged(NotifyCollectionChangedAction action, List items, int? index = null)
      {
        if (index.HasValue)
        {
            CollectionChanged(this, new NotifyCollectionChangedEventArgs(action, items, index.Value));
        }
        else
        {
            CollectionChanged(this, new NotifyCollectionChangedEventArgs(action, items));
        }
         OnPropertyChanged(GetPropertyName(() => Count));
      }

      protected virtual void OnPropertyChanged(string propName)
      {
        PropertyChanged(this, new PropertyChangedEventArgs(propName));
      }

      public new virtual void Clear()
      {
        base.Clear();
        OnCollectionChanged(NotifyCollectionChangedAction.Reset, null);
      }

      public new virtual T Pop()
      {
        var result = base.Pop();
        OnCollectionChanged(NotifyCollectionChangedAction.Remove, new List() { result }, base.Count);
        return result;
      }

      public new virtual void Push(T item)
      {
        base.Push(item);
        OnCollectionChanged(NotifyCollectionChangedAction.Add, new List() { item }, base.Count - 1);
      }   

      public new virtual void TrimExcess()
      {
        base.TrimExcess();
        OnPropertyChanged(GetPropertyName(() => Count));
      }

String GetPropertyName(Expression> propertyId)
{
   return ((MemberExpression)propertyId.Body).Member.Name;
}

    }

Ответ 4

Я понимаю, что уже есть несколько ответов, но подумал, что я бы немного ответил с моим. Я собрал все упомянутое в постах и комментариях. Было несколько вещей, которые побудили меня сделать это:

  • INPC должен всегда срабатывала для Count, когда Push, Pop или Clear называются, как уже упоминалось в одном из постов.
  • Для Clear, действие должно быть Reset а индекс для события изменения коллекции должен быть установлен на -1 (который по умолчанию будет установлен в любом случае, если не установлен, так что другие публикации имеют это): .NET docs
  • Для Push/Pop, действие должно быть Add/Remove а индекс для события изменения коллекции должен быть 0 для стека, который является всегда и только первым элементом, который может быть манипулирован (думаю stack.GetEnumerator().MoveNext()),
  • Предоставьте доступ ко всем трем конструкторам, доступным в Stack<T> и используйте вызовы base() поскольку нет причин переопределять логику.

Результаты в:

public class ObservableStack<T> : Stack<T>, INotifyCollectionChanged, INotifyPropertyChanged
{
    #region Constructors

    public ObservableStack() : base() { }

    public ObservableStack(IEnumerable<T> collection) : base(collection) { }

    public ObservableStack(int capacity) : base(capacity) { }

    #endregion

    #region Overrides

    public virtual new T Pop()
    {
        var item = base.Pop();
        OnCollectionChanged(NotifyCollectionChangedAction.Remove, item);

        return item;
    }

    public virtual new void Push(T item)
    {
        base.Push(item);
        OnCollectionChanged(NotifyCollectionChangedAction.Add, item);
    }

    public virtual new void Clear()
    {
        base.Clear();
        OnCollectionChanged(NotifyCollectionChangedAction.Reset, default);
    }

    #endregion

    #region CollectionChanged

    public virtual event NotifyCollectionChangedEventHandler CollectionChanged;

    protected virtual void OnCollectionChanged(NotifyCollectionChangedAction action, T item)
    {
        CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(
            action
            , item
            , item == null ? -1 : 0)
        );

        OnPropertyChanged(nameof(Count));
    }

    #endregion

    #region PropertyChanged

    public virtual event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string proertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(proertyName));
    }

    #endregion
}