Я ищу INotifyCollectionChanged
реализацию Stack
и Queue
. Я мог бы свернуть свой собственный, но я не хочу изобретать велосипед.
Наблюдаемый стек и очередь
Ответы (4)
Со стеками и очередями (почти по определению) у вас есть доступ только к вершине стека или голове очереди. Это то, что отличает их от List
. (Итак, поэтому вы не нашли его)
Чтобы ответить, хотя вы могли бы написать свой собственный, я бы сделал это, наследуя от ObservableCollection
, а затем в случае стека, реализующего Push
как Insert
со смещением 0 (и всплывающее окно как возвращающий индекс 0, затем RemoveAt
индекс 0); или с очередью вы могли бы просто Add
в конец списка до Enqueue
, а захватить и удалить первый элемент, как и со стеком, за Dequeue
. Операции Insert
, Add
и RemoveAt
будут вызываться для базового ObservableCollection
и вызывать событие CollectionChanged
.
Вы также можете сказать, что просто хотите привязать или получать уведомления, когда один элемент, к которому у вас должен быть доступ, изменяется. Вы должны снова создать свой собственный класс, производный от Stack или Queue, и запустить событие CollectionChanged вручную, когда:
- Что-то помещается в стек или выталкивается из него
- Что-то удалено из очереди
- Что-то поставлено в очередь в очереди, когда ранее очередь была пустой
ObservableStack
- получить из (или лучше содержать) ObservableCollection
. Второй подход был бы лучше для ObservableQueue
- получить от Queue
и реализовать свои собственные уведомления. Это связано с тем, что любой ObservableQueue
, построенный на List
, будет иметь производительность O(N) для Enqueue
или Dequeue
, тогда как все остальное будет O(1). Это может повлиять на производительность, если в очереди много элементов.
- person Stephen Cleary; 27.06.2010
Я столкнулся с той же проблемой и хочу поделиться своим решением с другими. Надеюсь, это кому-то поможет.
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
(из стека) всегда будет первым элементом?
- person dotNET; 21.09.2016
base.Count
, и 0
вызывают у меня исключение. :(
- person dotNET; 22.09.2016
Я понимаю, что уже есть несколько ответов, но решил немного отплатить своим. Я собрал все, что упоминалось в сообщениях и комментариях. Было несколько вещей, которые побудили меня сделать это:
- INPC всегда должен срабатывать для
Count
при вызовеPush
,Pop
илиClear
, как упоминалось в одном из сообщений. - Для
Clear
действие должно бытьReset
, а для индекса события изменения коллекции должно быть установлено значение-1
(которое по умолчанию все равно будет установлено, если оно не установлено, чтобы оно было в других сообщениях): документы .NET - Для
Push
/Pop
действие должно бытьAdd
/Remove
, а индекс для события изменения коллекции должен быть0
для стека, который всегда и только первый элемент, которым можно манипулировать (думаю,stack.GetEnumerator().MoveNext()
). - Открыты все 3 конструктора, доступные в
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
}
Очень похож на класс выше, за некоторыми исключениями:
- Опубликовать реквизит изменен для изменений коллекции для Count
- Переопределить TrimExcess() b/c, который может повлиять на Count
- Сделал события общедоступными, поэтому мне не нужно транслировать их в интерфейс
- Передает индекс в collectionchanged, когда это необходимо
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;
}
}