可观察的堆栈和队列

时间:2010-06-27 10:56:53

标签: c# stack queue inotifycollectionchanged

我正在寻找INotifyCollectionChangedStack的{​​{1}}实施。我可以自己动手,但我不想重新发明轮子。

4 个答案:

答案 0 :(得分:31)

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

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

答案 1 :(得分:9)

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

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


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

  • 某物被推入或从堆栈中弹出
  • 某些东西从队列中出队
  • 当队列先前为空时,队列中排队等待

答案 2 :(得分:1)

与上述类非常相似,但有一些例外:

  1. 发布道具已更改为计数
  2. 的集合更改
  3. 覆盖可能影响计数的TrimExcess()b / c
  4. 将事件设为公开,因此我无需转换到界面
  5. 在适当时将索引传递给collectionchanged
  6.     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;
    }
    
        }

答案 3 :(得分:0)

我意识到已经有一些答案了,但是我想我会给我一点点回报。我汇总了帖子和评论中提到的所有内容。很少有促使我这样做的事情:

  • 如其中一则文章所述,当调用CountPushPop时,INPC应始终为Clear触发。
  • 对于Clear,操作应为Reset,并且集合更改事件的索引应设置为-1(如果未设置,它将默认为默认值,因此其他帖子具有):.NET docs
  • 对于Push / Pop,对于堆栈,动作应为Add / Remove,并且集合更改事件的索引应为0始终是并且唯一可以被操纵的第一项(请考虑stack.GetEnumerator().MoveNext())。
  • 公开了Stack<T>中可用的所有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
}