Collezione osservabile veloce e filiforms

Notifiche di aumento di ObservableCollection per ogni azione eseguita su di esse. In primo luogo non hanno bulk aggiungere o rimuovere le chiamate, in secondo luogo non sono thread-safe.

Questo non li rende più lenti? Non possiamo avere un’alternativa più veloce? Alcuni dicono che ICollectionView avvolto attorno a una ObservableCollection è veloce? Quanto è vera questa affermazione.

ObservableCollection può essere veloce, se lo desidera . 🙂

Il codice sotto è un ottimo esempio di una collezione osservabile più veloce e sicura di thread e puoi estenderla in base ai tuoi desideri.

 using System.Collections.Specialized; public class FastObservableCollection : ObservableCollection { private readonly object locker = new object(); ///  /// This private variable holds the flag to /// turn on and off the collection changed notification. ///  private bool suspendCollectionChangeNotification; ///  /// Initializes a new instance of the FastObservableCollection class. ///  public FastObservableCollection() : base() { this.suspendCollectionChangeNotification = false; } ///  /// This event is overriden CollectionChanged event of the observable collection. ///  public override event NotifyCollectionChangedEventHandler CollectionChanged; ///  /// This method adds the given generic list of items /// as a range into current collection by casting them as type T. /// It then notifies once after all items are added. ///  /// The source collection. public void AddItems(IList items) { lock(locker) { this.SuspendCollectionChangeNotification(); foreach (var i in items) { InsertItem(Count, i); } this.NotifyChanges(); } } ///  /// Raises collection change event. ///  public void NotifyChanges() { this.ResumeCollectionChangeNotification(); var arg = new NotifyCollectionChangedEventArgs (NotifyCollectionChangedAction.Reset); this.OnCollectionChanged(arg); } ///  /// This method removes the given generic list of items as a range /// into current collection by casting them as type T. /// It then notifies once after all items are removed. ///  /// The source collection. public void RemoveItems(IList items) { lock(locker) { this.SuspendCollectionChangeNotification(); foreach (var i in items) { Remove(i); } this.NotifyChanges(); } } ///  /// Resumes collection changed notification. ///  public void ResumeCollectionChangeNotification() { this.suspendCollectionChangeNotification = false; } ///  /// Suspends collection changed notification. ///  public void SuspendCollectionChangeNotification() { this.suspendCollectionChangeNotification = true; } ///  /// This collection changed event performs thread safe event raising. ///  /// The event argument. protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e) { // Recommended is to avoid reentry // in collection changed event while collection // is getting changed on other thread. using (BlockReentrancy()) { if (!this.suspendCollectionChangeNotification) { NotifyCollectionChangedEventHandler eventHandler = this.CollectionChanged; if (eventHandler == null) { return; } // Walk thru invocation list. Delegate[] delegates = eventHandler.GetInvocationList(); foreach (NotifyCollectionChangedEventHandler handler in delegates) { // If the subscriber is a DispatcherObject and different thread. DispatcherObject dispatcherObject = handler.Target as DispatcherObject; if (dispatcherObject != null && !dispatcherObject.CheckAccess()) { // Invoke handler in the target dispatcher's thread... // asynchronously for better responsiveness. dispatcherObject.Dispatcher.BeginInvoke (DispatcherPriority.DataBind, handler, this, e); } else { // Execute handler as is. handler(this, e); } } } } } } 

Anche ICollectionView che si trova sopra ObservableCollection è triggersmente a conoscenza delle modifiche ed esegue il filtraggio, il raggruppamento, l’ordinamento relativamente veloce rispetto a qualsiasi altro elenco di fonti.

Ancora una volta le raccolte osservabili potrebbero non essere la risposta perfetta per aggiornamenti più rapidi dei dati, ma svolgono bene il loro lavoro.

Ecco una raccolta di alcune soluzioni che ho realizzato. L’idea della collezione ha cambiato la denominazione presa dalla prima risposta.

Sembra anche che l’operazione “Reset” dovrebbe essere sincrona con il thread principale, altrimenti accadono cose strane a CollectionView e CollectionViewSource.

Penso che sia perché sul gestore “Reset” tenta di leggere immediatamente il contenuto della raccolta e dovrebbero essere già sul posto. Se si esegue il “Reset” asincrono e si aggiungono immediatamente alcuni elementi anche asincroni rispetto agli elementi aggiunti di recente potrebbero essere aggiunti due volte.

 public interface IObservableList : IList, INotifyCollectionChanged { } public class ObservableList : IObservableList { private IList collection = new List(); public event NotifyCollectionChangedEventHandler CollectionChanged; private ReaderWriterLock sync = new ReaderWriterLock(); protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs args) { if (CollectionChanged == null) return; foreach (NotifyCollectionChangedEventHandler handler in CollectionChanged.GetInvocationList()) { // If the subscriber is a DispatcherObject and different thread. var dispatcherObject = handler.Target as DispatcherObject; if (dispatcherObject != null && !dispatcherObject.CheckAccess()) { if ( args.Action == NotifyCollectionChangedAction.Reset ) dispatcherObject.Dispatcher.Invoke (DispatcherPriority.DataBind, handler, this, args); else // Invoke handler in the target dispatcher's thread... // asynchronously for better responsiveness. dispatcherObject.Dispatcher.BeginInvoke (DispatcherPriority.DataBind, handler, this, args); } else { // Execute handler as is. handler(this, args); } } } public ObservableList() { } public void Add(T item) { sync.AcquireWriterLock(Timeout.Infinite); try { collection.Add(item); OnCollectionChanged( new NotifyCollectionChangedEventArgs( NotifyCollectionChangedAction.Add, item)); } finally { sync.ReleaseWriterLock(); } } public void Clear() { sync.AcquireWriterLock(Timeout.Infinite); try { collection.Clear(); OnCollectionChanged( new NotifyCollectionChangedEventArgs( NotifyCollectionChangedAction.Reset)); } finally { sync.ReleaseWriterLock(); } } public bool Contains(T item) { sync.AcquireReaderLock(Timeout.Infinite); try { var result = collection.Contains(item); return result; } finally { sync.ReleaseReaderLock(); } } public void CopyTo(T[] array, int arrayIndex) { sync.AcquireWriterLock(Timeout.Infinite); try { collection.CopyTo(array, arrayIndex); } finally { sync.ReleaseWriterLock(); } } public int Count { get { sync.AcquireReaderLock(Timeout.Infinite); try { return collection.Count; } finally { sync.ReleaseReaderLock(); } } } public bool IsReadOnly { get { return collection.IsReadOnly; } } public bool Remove(T item) { sync.AcquireWriterLock(Timeout.Infinite); try { var index = collection.IndexOf(item); if (index == -1) return false; var result = collection.Remove(item); if (result) OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, index)); return result; } finally { sync.ReleaseWriterLock(); } } public IEnumerator GetEnumerator() { return collection.GetEnumerator(); } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return collection.GetEnumerator(); } public int IndexOf(T item) { sync.AcquireReaderLock(Timeout.Infinite); try { var result = collection.IndexOf(item); return result; } finally { sync.ReleaseReaderLock(); } } public void Insert(int index, T item) { sync.AcquireWriterLock(Timeout.Infinite); try { collection.Insert(index, item); OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index)); } finally { sync.ReleaseWriterLock(); } } public void RemoveAt(int index) { sync.AcquireWriterLock(Timeout.Infinite); try { if (collection.Count == 0 || collection.Count < = index) return; var item = collection[index]; collection.RemoveAt(index); OnCollectionChanged( new NotifyCollectionChangedEventArgs( NotifyCollectionChangedAction.Remove, item, index)); } finally { sync.ReleaseWriterLock(); } } public T this[int index] { get { sync.AcquireReaderLock(Timeout.Infinite); try { var result = collection[index]; return result; } finally { sync.ReleaseReaderLock(); } } set { sync.AcquireWriterLock(Timeout.Infinite); try { if (collection.Count == 0 || collection.Count <= index) return; var item = collection[index]; collection[index] = value; OnCollectionChanged( new NotifyCollectionChangedEventArgs( NotifyCollectionChangedAction.Replace, value, item, index)); } finally { sync.ReleaseWriterLock(); } } } } 

Non riesco ad aggiungere commenti perché non sono ancora abbastanza interessante, ma la condivisione del problema che ho riscontrato vale probabilmente la pena di postare anche se non è davvero una risposta. Ho continuato a ricevere un’eccezione “Indice non compreso nell’intervallo” utilizzando questa FastObservableCollection, a causa di BeginInvoke. Apparentemente le modifiche notificate possono essere annullate prima che il gestore venga chiamato, quindi per risolvere questo problema ho passato quanto segue come quarto parametro per BeginInvoke chiamato dal metodo OnCollectionChanged (invece di usare l’event args uno):

 dispatcherObject.Dispatcher.BeginInvoke (DispatcherPriority.DataBind, handler, this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); 

Invece di questo:

 dispatcherObject.Dispatcher.BeginInvoke (DispatcherPriority.DataBind, handler, this, e); 

Ciò risolveva il problema di “Indice non compreso nell’intervallo” in cui stavo eseguendo. Ecco una spiegazione più dettagliata / codice snpipet: dove posso ottenere un CollectionView sicuro per i thread?

Un esempio in cui viene creato un elenco osservabile sincronizzato:

 newSeries = new XYChart.Series<>(); ObservableList> listaSerie; listaSerie = FXCollections.synchronizedObservableList(FXCollections.observableList(new ArrayList>())); newSeries.setData(listaSerie);