diff --git a/src/Avalonia.Base/Collections/AvaloniaList.cs b/src/Avalonia.Base/Collections/AvaloniaList.cs index 5c674be880..832bd01b43 100644 --- a/src/Avalonia.Base/Collections/AvaloniaList.cs +++ b/src/Avalonia.Base/Collections/AvaloniaList.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; using System.Linq; +using System.Runtime.InteropServices; using Avalonia.Diagnostics; namespace Avalonia.Collections @@ -742,6 +743,11 @@ namespace Avalonia.Collections NotifyCountChanged(); } + internal Span UnsafeAsSpan() + { + return CollectionsMarshal.AsSpan(_inner); + } + /// /// Enumerates the elements of a . /// diff --git a/src/Avalonia.Base/Compatibility/CollectionCompatibilityExtensions.cs b/src/Avalonia.Base/Compatibility/CollectionCompatibilityExtensions.cs index e22288a74d..6d017c7751 100644 --- a/src/Avalonia.Base/Compatibility/CollectionCompatibilityExtensions.cs +++ b/src/Avalonia.Base/Compatibility/CollectionCompatibilityExtensions.cs @@ -1,32 +1,44 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; - -namespace System; +using System.Runtime.CompilerServices; #if !NET6_0_OR_GREATER -internal static class CollectionCompatibilityExtensions +namespace System { - public static bool Remove( - this Dictionary o, - TKey key, - [MaybeNullWhen(false)] out TValue value) - where TKey : notnull + internal static class CollectionCompatibilityExtensions { - if (o.TryGetValue(key, out value)) - return o.Remove(key); - return false; - } + public static bool Remove( + this Dictionary o, + TKey key, + [MaybeNullWhen(false)] out TValue value) + where TKey : notnull + { + if (o.TryGetValue(key, out value)) + return o.Remove(key); + return false; + } - public static bool TryAdd(this Dictionary o, TKey key, TValue value) - where TKey : notnull + public static bool TryAdd(this Dictionary o, TKey key, TValue value) + where TKey : notnull + { + if (!o.ContainsKey(key)) + { + o.Add(key, value); + return true; + } + + return false; + } + } + + namespace Runtime.InteropServices { - if (!o.ContainsKey(key)) + public static class CollectionsMarshal { - o.Add(key, value); - return true; + public static Span AsSpan(List? list) + => list is null ? default : new Span(Unsafe.As>(list).Value, 0, list.Count); } - - return false; } } + #endif diff --git a/src/Avalonia.Base/StyledElement.cs b/src/Avalonia.Base/StyledElement.cs index b98e378338..851f591443 100644 --- a/src/Avalonia.Base/StyledElement.cs +++ b/src/Avalonia.Base/StyledElement.cs @@ -5,8 +5,10 @@ using System.Collections.Specialized; using System.ComponentModel; using System.Diagnostics; using System.Linq; +using System.Runtime.InteropServices; using Avalonia.Animation; using Avalonia.Collections; +using Avalonia.Collections.Pooled; using Avalonia.Controls; using Avalonia.Data; using Avalonia.Diagnostics; @@ -82,7 +84,7 @@ namespace Avalonia private string? _name; private Classes? _classes; private ILogicalRoot? _logicalRoot; - private IAvaloniaList? _logicalChildren; + private AvaloniaList? _logicalChildren; private IResourceDictionary? _resources; private Styles? _styles; private bool _stylesApplied; @@ -558,18 +560,13 @@ namespace Avalonia /// The event args. internal virtual void NotifyChildResourcesChanged(ResourcesChangedEventArgs e) { - if (_logicalChildren is object) + if (_logicalChildren is { Count: > 0 }) { - var count = _logicalChildren.Count; + e ??= ResourcesChangedEventArgs.Empty; - if (count > 0) + foreach (var child in _logicalChildren.UnsafeAsSpan()) { - e ??= ResourcesChangedEventArgs.Empty; - - for (var i = 0; i < count; ++i) - { - _logicalChildren[i].NotifyResourcesChanged(e); - } + child.NotifyResourcesChanged(e); } } } @@ -747,16 +744,16 @@ namespace Avalonia element._dataContextUpdating = true; element.OnDataContextBeginUpdate(); - var logicalChildren = element.LogicalChildren; - var logicalChildrenCount = logicalChildren.Count; - - for (var i = 0; i < logicalChildrenCount; i++) + if (element._logicalChildren is {} children) { - if (element.LogicalChildren[i] is StyledElement s && - s.InheritanceParent == element && - !s.IsSet(DataContextProperty)) + foreach (var child in children.UnsafeAsSpan()) { - DataContextNotifying(s, updateStarted); + if (child is StyledElement s && + s.InheritanceParent == element && + !s.IsSet(DataContextProperty)) + { + DataContextNotifying(s, updateStarted); + } } } } @@ -903,14 +900,14 @@ namespace Avalonia AttachedToLogicalTree?.Invoke(this, e); } - var logicalChildren = LogicalChildren; - var logicalChildrenCount = logicalChildren.Count; - - for (var i = 0; i < logicalChildrenCount; i++) + if (_logicalChildren is { } children) { - if (logicalChildren[i] is StyledElement child && child._logicalRoot != e.Root) // child may already have been attached within an event handler + foreach (var child in children.UnsafeAsSpan()) { - child.OnAttachedToLogicalTreeCore(e); + if (child is StyledElement s && s._logicalRoot != e.Root) + { + s.OnAttachedToLogicalTreeCore(e); + } } } } @@ -924,14 +921,14 @@ namespace Avalonia OnDetachedFromLogicalTree(e); DetachedFromLogicalTree?.Invoke(this, e); - var logicalChildren = LogicalChildren; - var logicalChildrenCount = logicalChildren.Count; - - for (var i = 0; i < logicalChildrenCount; i++) + if (_logicalChildren is { } children) { - if (logicalChildren[i] is StyledElement child) + foreach (var child in children.UnsafeAsSpan()) { - child.OnDetachedFromLogicalTreeCore(e); + if (child is StyledElement s) + { + s.OnDetachedFromLogicalTreeCore(e); + } } } @@ -991,11 +988,9 @@ namespace Avalonia if (_logicalChildren is not null) { - var childCount = _logicalChildren.Count; - - for (var i = 0; i < childCount; ++i) + foreach (var child in _logicalChildren.UnsafeAsSpan()) { - (_logicalChildren[i] as StyledElement)?.DetachStyles(styles); + (child as StyledElement)?.DetachStyles(styles); } } }