【发布时间】:2011-03-08 19:33:21
【问题描述】:
我正在寻找Stack 和Queue 的INotifyCollectionChanged 实现。我可以自己动手,但我不想重新发明轮子。
【问题讨论】:
标签: c# stack queue inotifycollectionchanged
我正在寻找Stack 和Queue 的INotifyCollectionChanged 实现。我可以自己动手,但我不想重新发明轮子。
【问题讨论】:
标签: c# stack queue inotifycollectionchanged
我遇到了同样的问题,想将我的解决方案分享给其他人。希望这对某人有所帮助。
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; }
}
}
【讨论】:
base.Count?不应该是0,因为在Pop(堆栈)的情况下更改的项目将始终是第一项?
使用堆栈和队列(几乎按照定义),您只能访问堆栈顶部或队列头部。这就是它们与List 的区别所在。 (所以,这就是你没有找到的原因)
虽然您可以自己编写答案,但我会从ObservableCollection 派生,然后在堆栈的情况下,将Push 作为Insert 在偏移量0 处实现(并作为返回索引0 弹出)然后RemoveAt索引0);或者使用队列,您可以将Add 放到Enqueue 的列表末尾,然后像堆栈一样为Dequeue 抓取并删除第一项。 Insert、Add 和 RemoveAt 操作将在底层 ObservableCollection 上调用,因此会触发 CollectionChanged 事件。
您可能还说,您只是想绑定或在您应该有权访问的一项更改时收到通知。您将再次创建自己的类,派生自 Stack 或 Queue,并在以下情况下手动触发 CollectionChanged 事件:
【讨论】:
ObservableStack 的第一种方法——派生自(或者更好的是,包含)ObservableCollection。第二种方法更适合ObservableQueue - 从Queue 派生并实现您自己的通知。这是因为任何基于List 构建的ObservableQueue 对于Enqueue 或Dequeue 的性能都是O(N),而其他一切都是O(1)。如果队列中有很多元素,这会对性能产生影响。
我意识到已经有一些答案,但我想我会回馈一点。我将帖子和 cmets 中提到的所有内容放在一起。有几件事促使我这样做:
Push、Pop 或 Clear 时,INPC 应始终为 Count 触发。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
}
【讨论】:
与上面的类非常相似,除了一些例外:
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;
}
}
【讨论】: