|
|
@@ -55,15 +55,15 @@ namespace Avalonia.Collections
|
|
|
/// </remarks>
|
|
|
public class AvaloniaList<T> : IAvaloniaList<T>, IList, INotifyCollectionChangedDebug
|
|
|
{
|
|
|
- private List<T> _inner;
|
|
|
+ private readonly List<T> _inner;
|
|
|
private NotifyCollectionChangedEventHandler _collectionChanged;
|
|
|
|
|
|
/// <summary>
|
|
|
/// Initializes a new instance of the <see cref="AvaloniaList{T}"/> class.
|
|
|
/// </summary>
|
|
|
public AvaloniaList()
|
|
|
- : this(Enumerable.Empty<T>())
|
|
|
{
|
|
|
+ _inner = new List<T>();
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
@@ -89,8 +89,8 @@ namespace Avalonia.Collections
|
|
|
/// </summary>
|
|
|
public event NotifyCollectionChangedEventHandler CollectionChanged
|
|
|
{
|
|
|
- add { _collectionChanged += value; }
|
|
|
- remove { _collectionChanged -= value; }
|
|
|
+ add => _collectionChanged += value;
|
|
|
+ remove => _collectionChanged -= value;
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
@@ -150,7 +150,7 @@ namespace Avalonia.Collections
|
|
|
|
|
|
T old = _inner[index];
|
|
|
|
|
|
- if (!object.Equals(old, value))
|
|
|
+ if (!EqualityComparer<T>.Default.Equals(old, value))
|
|
|
{
|
|
|
_inner[index] = value;
|
|
|
|
|
|
@@ -187,45 +187,38 @@ namespace Avalonia.Collections
|
|
|
Validate?.Invoke(item);
|
|
|
int index = _inner.Count;
|
|
|
_inner.Add(item);
|
|
|
- NotifyAdd(new[] { item }, index);
|
|
|
+ NotifyAdd(item, index);
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// Adds multiple items to the collection.
|
|
|
/// </summary>
|
|
|
/// <param name="items">The items.</param>
|
|
|
- public virtual void AddRange(IEnumerable<T> items)
|
|
|
- {
|
|
|
- Contract.Requires<ArgumentNullException>(items != null);
|
|
|
-
|
|
|
- var list = (items as IList) ?? items.ToList();
|
|
|
-
|
|
|
- if (list.Count > 0)
|
|
|
- {
|
|
|
- if (Validate != null)
|
|
|
- {
|
|
|
- foreach (var item in list)
|
|
|
- {
|
|
|
- Validate((T)item);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- int index = _inner.Count;
|
|
|
- _inner.AddRange(items);
|
|
|
- NotifyAdd(list, index);
|
|
|
- }
|
|
|
- }
|
|
|
+ public virtual void AddRange(IEnumerable<T> items) => InsertRange(_inner.Count, items);
|
|
|
|
|
|
/// <summary>
|
|
|
/// Removes all items from the collection.
|
|
|
/// </summary>
|
|
|
public virtual void Clear()
|
|
|
{
|
|
|
- if (this.Count > 0)
|
|
|
+ if (Count > 0)
|
|
|
{
|
|
|
- var old = _inner;
|
|
|
- _inner = new List<T>();
|
|
|
- NotifyReset(old);
|
|
|
+ if (_collectionChanged != null)
|
|
|
+ {
|
|
|
+ var e = ResetBehavior == ResetBehavior.Reset ?
|
|
|
+ EventArgsCache.ResetCollectionChanged :
|
|
|
+ new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, _inner.ToList(), 0);
|
|
|
+
|
|
|
+ _inner.Clear();
|
|
|
+
|
|
|
+ _collectionChanged(this, e);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ _inner.Clear();
|
|
|
+ }
|
|
|
+
|
|
|
+ NotifyCountChanged();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -253,9 +246,20 @@ namespace Avalonia.Collections
|
|
|
/// Returns an enumerator that enumerates the items in the collection.
|
|
|
/// </summary>
|
|
|
/// <returns>An <see cref="IEnumerator{T}"/>.</returns>
|
|
|
- public IEnumerator<T> GetEnumerator()
|
|
|
+ IEnumerator<T> IEnumerable<T>.GetEnumerator()
|
|
|
{
|
|
|
- return _inner.GetEnumerator();
|
|
|
+ return new Enumerator(_inner);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <inheritdoc/>
|
|
|
+ IEnumerator IEnumerable.GetEnumerator()
|
|
|
+ {
|
|
|
+ return new Enumerator(_inner);
|
|
|
+ }
|
|
|
+
|
|
|
+ public Enumerator GetEnumerator()
|
|
|
+ {
|
|
|
+ return new Enumerator(_inner);
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
@@ -289,7 +293,7 @@ namespace Avalonia.Collections
|
|
|
{
|
|
|
Validate?.Invoke(item);
|
|
|
_inner.Insert(index, item);
|
|
|
- NotifyAdd(new[] { item }, index);
|
|
|
+ NotifyAdd(item, index);
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
@@ -301,20 +305,83 @@ namespace Avalonia.Collections
|
|
|
{
|
|
|
Contract.Requires<ArgumentNullException>(items != null);
|
|
|
|
|
|
- var list = (items as IList) ?? items.ToList();
|
|
|
+ bool willRaiseCollectionChanged = _collectionChanged != null;
|
|
|
+ bool hasValidation = Validate != null;
|
|
|
|
|
|
- if (list.Count > 0)
|
|
|
+ if (items is IList list)
|
|
|
{
|
|
|
- if (Validate != null)
|
|
|
+ if (list.Count > 0)
|
|
|
{
|
|
|
- foreach (var item in list)
|
|
|
+ if (list is ICollection<T> collection)
|
|
|
{
|
|
|
- Validate((T)item);
|
|
|
+ if (hasValidation)
|
|
|
+ {
|
|
|
+ foreach (T item in collection)
|
|
|
+ {
|
|
|
+ Validate(item);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ _inner.InsertRange(index, collection);
|
|
|
+ NotifyAdd(list, index);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ using (IEnumerator<T> en = items.GetEnumerator())
|
|
|
+ {
|
|
|
+ int insertIndex = index;
|
|
|
+
|
|
|
+ while (en.MoveNext())
|
|
|
+ {
|
|
|
+ T item = en.Current;
|
|
|
+
|
|
|
+ if (hasValidation)
|
|
|
+ {
|
|
|
+ Validate(item);
|
|
|
+ }
|
|
|
+
|
|
|
+ _inner.Insert(insertIndex++, item);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ NotifyAdd(list, index);
|
|
|
}
|
|
|
}
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ using (IEnumerator<T> en = items.GetEnumerator())
|
|
|
+ {
|
|
|
+ if (en.MoveNext())
|
|
|
+ {
|
|
|
+ // Avoid allocating list for collection notification if there is no event subscriptions.
|
|
|
+ List<T> notificationItems = willRaiseCollectionChanged ?
|
|
|
+ new List<T>() :
|
|
|
+ null;
|
|
|
+
|
|
|
+ int insertIndex = index;
|
|
|
+
|
|
|
+ do
|
|
|
+ {
|
|
|
+ T item = en.Current;
|
|
|
+
|
|
|
+ if (hasValidation)
|
|
|
+ {
|
|
|
+ Validate(item);
|
|
|
+ }
|
|
|
|
|
|
- _inner.InsertRange(index, items);
|
|
|
- NotifyAdd((items as IList) ?? items.ToList(), index);
|
|
|
+ _inner.Insert(insertIndex++, item);
|
|
|
+
|
|
|
+ if (willRaiseCollectionChanged)
|
|
|
+ {
|
|
|
+ notificationItems.Add(item);
|
|
|
+ }
|
|
|
+
|
|
|
+ } while (en.MoveNext());
|
|
|
+
|
|
|
+ NotifyAdd(notificationItems, index);
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -382,7 +449,7 @@ namespace Avalonia.Collections
|
|
|
if (index != -1)
|
|
|
{
|
|
|
_inner.RemoveAt(index);
|
|
|
- NotifyRemove(new[] { item }, index);
|
|
|
+ NotifyRemove(item , index);
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
@@ -412,7 +479,7 @@ namespace Avalonia.Collections
|
|
|
{
|
|
|
T item = _inner[index];
|
|
|
_inner.RemoveAt(index);
|
|
|
- NotifyRemove(new[] { item }, index);
|
|
|
+ NotifyRemove(item , index);
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
@@ -480,12 +547,6 @@ namespace Avalonia.Collections
|
|
|
_inner.CopyTo((T[])array, index);
|
|
|
}
|
|
|
|
|
|
- /// <inheritdoc/>
|
|
|
- IEnumerator IEnumerable.GetEnumerator()
|
|
|
- {
|
|
|
- return _inner.GetEnumerator();
|
|
|
- }
|
|
|
-
|
|
|
/// <inheritdoc/>
|
|
|
Delegate[] INotifyCollectionChangedDebug.GetCollectionChangedSubscribers() => _collectionChanged?.GetInvocationList();
|
|
|
|
|
|
@@ -505,13 +566,29 @@ namespace Avalonia.Collections
|
|
|
NotifyCountChanged();
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Raises the <see cref="CollectionChanged"/> event with a add action.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="item">The item that was added.</param>
|
|
|
+ /// <param name="index">The starting index.</param>
|
|
|
+ private void NotifyAdd(T item, int index)
|
|
|
+ {
|
|
|
+ if (_collectionChanged != null)
|
|
|
+ {
|
|
|
+ var e = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, new[] { item }, index);
|
|
|
+ _collectionChanged(this, e);
|
|
|
+ }
|
|
|
+
|
|
|
+ NotifyCountChanged();
|
|
|
+ }
|
|
|
+
|
|
|
/// <summary>
|
|
|
/// Raises the <see cref="PropertyChanged"/> event when the <see cref="Count"/> property
|
|
|
/// changes.
|
|
|
/// </summary>
|
|
|
private void NotifyCountChanged()
|
|
|
{
|
|
|
- PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Count)));
|
|
|
+ PropertyChanged?.Invoke(this, EventArgsCache.CountPropertyChanged);
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
@@ -531,23 +608,57 @@ namespace Avalonia.Collections
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
- /// Raises the <see cref="CollectionChanged"/> event with a reset action.
|
|
|
+ /// Raises the <see cref="CollectionChanged"/> event with a remove action.
|
|
|
/// </summary>
|
|
|
- /// <param name="t">The items that were removed.</param>
|
|
|
- private void NotifyReset(IList t)
|
|
|
+ /// <param name="item">The item that was removed.</param>
|
|
|
+ /// <param name="index">The starting index.</param>
|
|
|
+ private void NotifyRemove(T item, int index)
|
|
|
{
|
|
|
if (_collectionChanged != null)
|
|
|
{
|
|
|
- NotifyCollectionChangedEventArgs e;
|
|
|
-
|
|
|
- e = ResetBehavior == ResetBehavior.Reset ?
|
|
|
- new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset) :
|
|
|
- new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, t, 0);
|
|
|
-
|
|
|
+ var e = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, new[] { item }, index);
|
|
|
_collectionChanged(this, e);
|
|
|
}
|
|
|
|
|
|
NotifyCountChanged();
|
|
|
}
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Enumerates the elements of a <see cref="AvaloniaList{T}"/>.
|
|
|
+ /// </summary>
|
|
|
+ public struct Enumerator : IEnumerator<T>
|
|
|
+ {
|
|
|
+ private List<T>.Enumerator _innerEnumerator;
|
|
|
+
|
|
|
+ public Enumerator(List<T> inner)
|
|
|
+ {
|
|
|
+ _innerEnumerator = inner.GetEnumerator();
|
|
|
+ }
|
|
|
+
|
|
|
+ public bool MoveNext()
|
|
|
+ {
|
|
|
+ return _innerEnumerator.MoveNext();
|
|
|
+ }
|
|
|
+
|
|
|
+ void IEnumerator.Reset()
|
|
|
+ {
|
|
|
+ ((IEnumerator)_innerEnumerator).Reset();
|
|
|
+ }
|
|
|
+
|
|
|
+ public T Current => _innerEnumerator.Current;
|
|
|
+
|
|
|
+ object IEnumerator.Current => Current;
|
|
|
+
|
|
|
+ public void Dispose()
|
|
|
+ {
|
|
|
+ _innerEnumerator.Dispose();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ internal static class EventArgsCache
|
|
|
+ {
|
|
|
+ internal static readonly PropertyChangedEventArgs CountPropertyChanged = new PropertyChangedEventArgs(nameof(AvaloniaList<object>.Count));
|
|
|
+ internal static readonly NotifyCollectionChangedEventArgs ResetCollectionChanged = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
|
|
|
}
|
|
|
}
|