【问题标题】:Observable Stack and Queue可观察的堆栈和队列
【发布时间】:2011-03-08 19:33:21
【问题描述】:

我正在寻找StackQueueINotifyCollectionChanged 实现。我可以自己动手,但我不想重新发明轮子。

【问题讨论】:

    标签: c# stack queue inotifycollectionchanged


    【解决方案1】:

    我遇到了同样的问题,想将我的解决方案分享给其他人。希望这对某人有所帮助。

    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; }
        }
    }
    

    【讨论】:

    • 嗨。在 Pop()“集合删除事件必须指定项目位置”之后出现错误。有任何解决这个问题的方法吗? tnx
    • base.Count 作为丢失的项目位置为我修复了它。 public new virtual T Pop() { var item = base.Pop(); this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, base.Count));归还物品; }
    • 我更喜欢这个解决方案而不是接受的答案。它使您可以维护堆栈/队列的性能预期和语义,而不仅仅是使用列表来模拟它(例如,与队列相比,从一开始就删除它的成本很高)。
    • 这确实对某人有帮助..像我一样..:) +1
    • @uli78: base.Count?不应该是0,因为在Pop(堆栈)的情况下更改的项目将始终是第一项?
    【解决方案2】:

    使用堆栈和队列(几乎按照定义),您只能访问堆栈顶部或队列头部。这就是它们与List 的区别所在。 (所以,这就是你没有找到的原因)

    虽然您可以自己编写答案,但我会从ObservableCollection 派生,然后在堆栈的情况下,将Push 作为Insert 在偏移量0 处实现(并作为返回索引0 弹出)然后RemoveAt索引0);或者使用队列,您可以将Add 放到Enqueue 的列表末尾,然后像堆栈一样为Dequeue 抓取并删除第一项。 InsertAddRemoveAt 操作将在底层 ObservableCollection 上调用,因此会触发 CollectionChanged 事件。


    您可能还说,您只是想绑定或在您应该有权访问的一项更改时收到通知。您将再次创建自己的类,派生自 Stack 或 Queue,并在以下情况下手动触发 CollectionChanged 事件:

    • 某些东西被压入堆栈或从堆栈中弹出
    • 某事从队列中出列
    • 当队列之前是空的时,队列中有东西在排队

    【讨论】:

    • 我推荐ObservableStack 的第一种方法——派生自(或者更好的是,包含)ObservableCollection。第二种方法更适合ObservableQueue - 从Queue 派生并实现您自己的通知。这是因为任何基于List 构建的ObservableQueue 对于EnqueueDequeue 的性能都是O(N),而其他一切都是O(1)。如果队列中有很多元素,这会对性能产生影响。
    • 我决定创建一个简单地实现 INotifyCollectionChanged 的​​通用可观察类。类调用内部 Stack 和 Queue 方法并引发适当的事件。由于 Stack 和 Queue 方法不是虚拟的(我无法理解为什么),因此更喜欢组合而不是继承。
    【解决方案3】:

    我意识到已经有一些答案,但我想我会回馈一点。我将帖子和 cmets 中提到的所有内容放在一起。有几件事促使我这样做:

    • 如其中一篇文章所述,当调用 PushPopClear 时,INPC 应始终为 Count 触发。
    • 对于Clear,操作应为Reset,集合更改事件的索引应设置为-1(如果未设置,则默认为无论如何其他帖子都有):.NET docs
    • 对于Push/Pop,操作应为Add/Remove,集合更改事件的索引应为0,因为堆栈始终是并且只有第一项可以操纵(想想stack.GetEnumerator().MoveNext())。
    • 公开Stack&lt;T&gt; 中可用的所有3 个构造函数并使用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
    }
    

    【讨论】:

      【解决方案4】:

      与上面的类非常相似,除了一些例外:

      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;
      }
      
          }

      【讨论】:

      • 我添加了一个本地实现来代替 CLRExtensions。我希望这不会太过分。
      猜你喜欢
      • 1970-01-01
      • 2013-09-18
      • 1970-01-01
      • 2014-01-28
      • 1970-01-01
      • 2014-07-21
      • 2015-11-16
      • 2019-03-07
      • 2021-04-29
      相关资源
      最近更新 更多