Skip to content

Commit

Permalink
Refactored code.
Browse files Browse the repository at this point in the history
  • Loading branch information
mahara committed Nov 11, 2024
1 parent d2801d7 commit 58eaa36
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 36 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
global using System;
global using System.Collections.Generic;
global using System.Collections.Specialized;

global using Iesi.Collections.Generic;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
global using System;
global using System.Collections;
global using System.Collections.Generic;
global using System.Collections.Specialized;


Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
global using System;
global using System.Collections;
global using System.Collections.Generic;
global using System.Collections.Specialized;
global using System.Linq;

global using NUnit.Framework;

Expand Down
156 changes: 130 additions & 26 deletions src/NHibernate.ObservableCollections/ObservableCollection.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
namespace Iesi.Collections.Generic;

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Runtime.Serialization;
Expand Down Expand Up @@ -34,6 +35,12 @@ public class ObservableCollection<T> :
Collection<T>,
INotifyCollectionChanged, INotifyPropertyChanged
{
[NonSerialized]
protected readonly List<NotifyCollectionChangedEventHandler> CollectionChangedEventHandlersCore = [];

[NonSerialized]
protected readonly List<PropertyChangedEventHandler> PropertyChangedEventHandlersCore = [];

private SimpleMonitor? _monitor; // Lazily allocated only when a subclass calls BlockReentrancy() or during serialization. Do not rename (binary serialization)

[NonSerialized]
Expand Down Expand Up @@ -84,8 +91,14 @@ public ObservableCollection(List<T> list) :
/// <summary>
/// Occurs when the collection changes, either by adding or removing an item.
/// </summary>
[field: NonSerialized]
public virtual event NotifyCollectionChangedEventHandler? CollectionChanged;
public event NotifyCollectionChangedEventHandler? CollectionChanged
{
add => CollectionChangedEventHandlersCore.Add(value!);
remove => CollectionChangedEventHandlersCore.Remove(value!);
}

public IReadOnlyCollection<NotifyCollectionChangedEventHandler> CollectionChangedEventHandlers =>
new ReadOnlyCollection<NotifyCollectionChangedEventHandler>(CollectionChangedEventHandlersCore);

/// <summary>
/// PropertyChanged event (per <see cref="INotifyPropertyChanged" />).
Expand All @@ -96,17 +109,40 @@ event PropertyChangedEventHandler? INotifyPropertyChanged.PropertyChanged
remove => PropertyChanged -= value;
}

protected event PropertyChangedEventHandler? PropertyChanged
{
add => PropertyChangedEventHandlersCore.Add(value!);
remove => PropertyChangedEventHandlersCore.Remove(value!);
}

public IReadOnlyCollection<PropertyChangedEventHandler> PropertyChangedEventHandlers =>
new ReadOnlyCollection<PropertyChangedEventHandler>(PropertyChangedEventHandlersCore);

/// <summary>
/// PropertyChanged event (per <see cref="INotifyPropertyChanged" />).
/// Raises a change notification indicating that all bindings should be refreshed.
/// </summary>
[field: NonSerialized]
protected virtual event PropertyChangedEventHandler? PropertyChanged;
public void Refresh()
{
RefreshCore();
}

protected virtual void RefreshCore()
{
OnCountPropertyChanged();
OnIndexerPropertyChanged();
OnCollectionReset();
}

/// <summary>
/// Called by base class <see cref="Collection{T}" /> when an item is added to collection;
/// raises a <see cref="CollectionChanged" /> event to any listeners.
/// </summary>
protected override void InsertItem(int index, T item)
{
InsertItemCore(index, item);
}

protected virtual void InsertItemCore(int index, T item)
{
CheckReentrancy();

Expand All @@ -122,6 +158,11 @@ protected override void InsertItem(int index, T item)
/// raises a <see cref="CollectionChanged" /> event to any listeners.
/// </summary>
protected override void RemoveItem(int index)
{
RemoveItemCore(index);
}

protected virtual void RemoveItemCore(int index)
{
CheckReentrancy();

Expand All @@ -138,6 +179,11 @@ protected override void RemoveItem(int index)
/// raises a <see cref="CollectionChanged" /> event to any listeners.
/// </summary>
protected override void SetItem(int index, T item)
{
SetItemCore(index, item);
}

protected virtual void SetItemCore(int index, T item)
{
CheckReentrancy();

Expand All @@ -161,6 +207,11 @@ public void Move(int oldIndex, int newIndex)
/// raises a <see cref="CollectionChanged" /> event to any listeners.
/// </summary>
protected virtual void MoveItem(int oldIndex, int newIndex)
{
MoveItemCore(oldIndex, newIndex);
}

protected virtual void MoveItemCore(int oldIndex, int newIndex)
{
CheckReentrancy();

Expand All @@ -177,6 +228,11 @@ protected virtual void MoveItem(int oldIndex, int newIndex)
/// raises a <see cref="CollectionChanged" /> event to any listeners.
/// </summary>
protected override void ClearItems()
{
ClearItemsCore();
}

protected virtual void ClearItemsCore()
{
using var _ = BlockReentrancy();
using var __ = DeferEventNotifications();
Expand All @@ -196,6 +252,11 @@ protected override void ClearItems()
/// </summary>
/// <param name="collection"></param>
public void AddRange(IEnumerable<T> collection)
{
AddRangeCore(collection);
}

protected virtual void AddRangeCore(IEnumerable<T> collection)
{
using var _ = BlockReentrancy();
using var __ = DeferEventNotifications();
Expand All @@ -210,6 +271,11 @@ public void AddRange(IEnumerable<T> collection)
/// <param name="index"></param>
/// <param name="collection"></param>
public void InsertRange(int index, IEnumerable<T> collection)
{
InsertRangeCore(index, collection);
}

protected virtual void InsertRangeCore(int index, IEnumerable<T> collection)
{
using var _ = BlockReentrancy();
using var __ = DeferEventNotifications();
Expand All @@ -223,6 +289,11 @@ public void InsertRange(int index, IEnumerable<T> collection)
/// </summary>
/// <param name="collection"></param>
public void RemoveRange(IEnumerable<T> collection)
{
RemoveRangeCore(collection);
}

protected virtual void RemoveRangeCore(IEnumerable<T> collection)
{
using var _ = BlockReentrancy();
using var __ = DeferEventNotifications();
Expand All @@ -237,6 +308,11 @@ public void RemoveRange(IEnumerable<T> collection)
/// <param name="index"></param>
/// <param name="count"></param>
public void RemoveRange(int index, int count)
{
RemoveRangeCore(index, count);
}

protected virtual void RemoveRangeCore(int index, int count)
{
using var _ = BlockReentrancy();
using var __ = DeferEventNotifications();
Expand All @@ -252,6 +328,11 @@ public void RemoveRange(int index, int count)
/// <param name="count"></param>
/// <param name="collection"></param>
public void ReplaceRange(int index, int count, IEnumerable<T> collection)
{
ReplaceRangeCore(index, count, collection);
}

protected virtual void ReplaceRangeCore(int index, int count, IEnumerable<T> collection)
{
using var _ = BlockReentrancy();
using var __ = DeferEventNotifications();
Expand All @@ -261,6 +342,11 @@ public void ReplaceRange(int index, int count, IEnumerable<T> collection)
}

protected virtual void InsertItemsRange(int index, IEnumerable<T> collection)
{
InsertItemsRangeCore(index, collection);
}

protected virtual void InsertItemsRangeCore(int index, IEnumerable<T> collection)
{
if (index < 0 || index > Count)
{
Expand Down Expand Up @@ -295,6 +381,11 @@ protected virtual void InsertItemsRange(int index, IEnumerable<T> collection)
}

protected virtual void RemoveItemsRange(IEnumerable<T> collection)
{
RemoveItemsRangeCore(collection);
}

protected virtual void RemoveItemsRangeCore(IEnumerable<T> collection)
{
if (collection is null)
{
Expand Down Expand Up @@ -368,6 +459,11 @@ protected virtual void RemoveItemsRange(IEnumerable<T> collection)
}

protected virtual void RemoveItemsRange(int index, int count)
{
RemoveItemsRangeCore(index, count);
}

protected virtual void RemoveItemsRangeCore(int index, int count)
{
if (index < 0 || index > Count || index + count > Count)
{
Expand Down Expand Up @@ -397,6 +493,14 @@ protected virtual IDisposable DeferEventNotifications()
return new DeferredEventArgsCollection(this);
}

/// <summary>
/// Helper to raise a ranged <see cref="CollectionChanged" /> event with action == Reset to any listeners.
/// </summary>
protected virtual void OnCollectionReset()
{
OnCollectionChanged(EventArgsCache.ResetCollectionChanged);
}

/// <summary>
/// Raises a <see cref="CollectionChanged" /> event to any listeners.
/// Properties/methods modifying this <see cref="ObservableCollection{T}" /> instance will raise
Expand All @@ -415,15 +519,17 @@ protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
return;
}

var handler = CollectionChanged;
if (handler is not null)
if (CollectionChangedEventHandlersCore is ICollection<NotifyCollectionChangedEventHandler> handlers)
{
// Not calling BlockReentrancy() here to avoid the SimpleMonitor allocation.
_blockReentrancyCount++;

try
{
handler(this, e);
foreach (var handler in handlers)
{
handler(this, e);
}
}
finally
{
Expand Down Expand Up @@ -465,35 +571,33 @@ private void OnCollectionChanged(NotifyCollectionChangedAction action, IList ite
}

/// <summary>
/// Helper to raise a ranged <see cref="CollectionChanged" /> event with action == Reset to any listeners.
/// </summary>
private void OnCollectionReset()
{
OnCollectionChanged(EventArgsCache.ResetCollectionChanged);
}

/// <summary>
/// Raises a PropertyChanged event (per <see cref="INotifyPropertyChanged" />).
/// Helper to raise a PropertyChanged event for the Count property.
/// </summary>
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
protected virtual void OnCountPropertyChanged()
{
PropertyChanged?.Invoke(this, e);
OnPropertyChanged(EventArgsCache.CountPropertyChanged);
}

/// <summary>
/// Helper to raise a PropertyChanged event for the Count property.
/// Helper to raise a PropertyChanged event for the Indexer property.
/// </summary>
private void OnCountPropertyChanged()
protected virtual void OnIndexerPropertyChanged()
{
OnPropertyChanged(EventArgsCache.CountPropertyChanged);
OnPropertyChanged(EventArgsCache.IndexerPropertyChanged);
}

/// <summary>
/// Helper to raise a PropertyChanged event for the Indexer property.
/// Raises a PropertyChanged event (per <see cref="INotifyPropertyChanged" />).
/// </summary>
private void OnIndexerPropertyChanged()
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
OnPropertyChanged(EventArgsCache.IndexerPropertyChanged);
if (PropertyChangedEventHandlersCore is ICollection<PropertyChangedEventHandler> handlers)
{
foreach (var handler in handlers)
{
handler(this, e);
}
}
}

[OnSerializing]
Expand Down Expand Up @@ -549,7 +653,7 @@ protected void CheckReentrancy()
// the problem only arises if reentrant changes make the original event args
// invalid for later listeners.
// This keeps existing code working (e.g. Selector.SelectedItems).
if (CollectionChanged?.GetInvocationList().Length > 1)
if (PropertyChangedEventHandlersCore.Count > 1)
{
throw new InvalidOperationException(SR.ObservableCollectionReentrancyNotAllowed);
}
Expand Down
2 changes: 0 additions & 2 deletions src/NHibernate.ObservableCollections/Properties/Usings.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
global using System;
global using System.Collections;
global using System.Collections.Generic;
global using System.Collections.Specialized;
global using System.ComponentModel;

Expand Down

0 comments on commit 58eaa36

Please sign in to comment.