diff --git a/Directory.Build.props b/Directory.Build.props index 649c7114..347d4395 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -10,12 +10,14 @@ false - + + true nullable - + + $(NoWarn);8604;8602 diff --git a/src/Avalonia.Controls.TreeDataGrid/Experimental/Data/Core/LightweightObservableBase.cs b/src/Avalonia.Controls.TreeDataGrid/Experimental/Data/Core/LightweightObservableBase.cs index 88f58b78..1caed2a1 100644 --- a/src/Avalonia.Controls.TreeDataGrid/Experimental/Data/Core/LightweightObservableBase.cs +++ b/src/Avalonia.Controls.TreeDataGrid/Experimental/Data/Core/LightweightObservableBase.cs @@ -1,4 +1,5 @@ -using System; +using System; +using System.Buffers; using System.Collections.Generic; using System.Threading; @@ -19,7 +20,7 @@ public abstract class LightweightObservableBase : IObservable private List>? _observers = []; public bool HasObservers => _observers?.Count > 0; - + public IDisposable Subscribe(IObserver observer) { _ = observer ?? throw new ArgumentNullException(nameof(observer)); @@ -117,32 +118,61 @@ protected void PublishNext(T value) if (Volatile.Read(ref _observers) != null) { IObserver[]? observers = null; - IObserver? singleObserver = null; + int count = 0; + + // Optimize for the common case of 1/2/3 observers. + IObserver? observer0 = null; + IObserver? observer1 = null; + IObserver? observer2 = null; lock (this) { if (_observers == null) { return; } - if (_observers.Count == 1) - { - singleObserver = _observers[0]; - } - else + + count = _observers.Count; + switch (count) { - observers = _observers.ToArray(); + case 3: + observer0 = _observers[0]; + observer1 = _observers[1]; + observer2 = _observers[2]; + break; + case 2: + observer0 = _observers[0]; + observer1 = _observers[1]; + break; + case 1: + observer0 = _observers[0]; + break; + case 0: + return; + default: + { + observers = ArrayPool>.Shared.Rent(count); + _observers.CopyTo(observers); + break; + } } } - if (singleObserver != null) + + if (observer0 != null) { - singleObserver.OnNext(value); + observer0.OnNext(value); + observer1?.OnNext(value); + observer2?.OnNext(value); } - else + else if (observers != null) { - foreach (var observer in observers!) + for (int i = 0; i < count; i++) { - observer.OnNext(value); + observers[i].OnNext(value); + // Avoid memory leak by clearing the reference. + observers[i] = null!; } + + ArrayPool>.Shared.Return(observers); } } } diff --git a/src/Avalonia.Controls.TreeDataGrid/Primitives/TreeDataGridPresenterBase.cs b/src/Avalonia.Controls.TreeDataGrid/Primitives/TreeDataGridPresenterBase.cs index b796da24..beb18c7f 100644 --- a/src/Avalonia.Controls.TreeDataGrid/Primitives/TreeDataGridPresenterBase.cs +++ b/src/Avalonia.Controls.TreeDataGrid/Primitives/TreeDataGridPresenterBase.cs @@ -739,8 +739,8 @@ private void RecycleElementOnItemRemoved(Control element) _focusedIndex = -1; } - UnrealizeElementOnItemRemoved(element); element.IsVisible = false; + UnrealizeElementOnItemRemoved(element); ElementFactory!.RecycleElement(element); _scrollViewer?.UnregisterAnchorCandidate(element); } diff --git a/src/Avalonia.Controls.TreeDataGrid/Primitives/TreeDataGridTextCell.cs b/src/Avalonia.Controls.TreeDataGrid/Primitives/TreeDataGridTextCell.cs index 189e2317..168eebcb 100644 --- a/src/Avalonia.Controls.TreeDataGrid/Primitives/TreeDataGridTextCell.cs +++ b/src/Avalonia.Controls.TreeDataGrid/Primitives/TreeDataGridTextCell.cs @@ -1,6 +1,4 @@ using System.ComponentModel; -using System.Globalization; -using System.Reflection; using Avalonia.Controls.Models.TreeDataGrid; using Avalonia.Controls.Selection; using Avalonia.Media;