refactor: use LRU for control cache

This commit is contained in:
estrogen elf
2025-06-16 01:00:27 -05:00
parent a74c18ac39
commit 8c8befe9ca
2 changed files with 49 additions and 4 deletions

View File

@@ -27,6 +27,7 @@
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.2.8" />
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.2.8" />
<PackageReference Include="AvaloniaEdit.TextMate" Version="11.3.0" />
<PackageReference Include="BitFaster.Caching" Version="2.5.3" />
<PackageReference Include="BlossomiShymae.Briar" Version="0.2.0" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
<PackageReference Include="DebounceThrottle" Version="3.0.1" />

View File

@@ -1,8 +1,12 @@
using Avalonia.Controls;
using Avalonia.Controls.Templates;
using Avalonia.VisualTree;
using BitFaster.Caching;
using BitFaster.Caching.Lru;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
@@ -10,7 +14,46 @@ namespace Needlework.Net
{
public class ViewLocator : IDataTemplate
{
private readonly Dictionary<object, Control> _controlCache = [];
private class ObjectComparer : IEqualityComparer<object>
{
public new bool Equals(object? x, object? y)
{
if (ReferenceEquals(x, y))
return true;
if (x == null || y == null)
return false;
return x.Equals(y);
}
public int GetHashCode([DisallowNull] object obj)
{
return obj.GetHashCode();
}
}
private readonly ICache<object, Control> _controlCache = new ConcurrentLruBuilder<object, Control>()
.WithExpireAfterAccess(TimeSpan.FromMinutes(5))
.WithKeyComparer(new ObjectComparer())
.WithCapacity(1024)
.WithMetrics()
.Build();
public ViewLocator()
{
_controlCache.Events.Value!.ItemRemoved += (source, args) =>
{
var descendants = args.Value!.GetVisualDescendants();
foreach (var descendant in descendants)
{
if (descendant.DataContext is INotifyPropertyChanged key)
{
_controlCache.TryRemove(key, out _);
}
}
};
}
public Control Build(object? data)
{
@@ -34,13 +77,14 @@ namespace Needlework.Net
{
throw new Exception("Data type has no view.");
}
if (!_controlCache.TryGetValue(data!, out var res))
bool isCold = !_controlCache.TryGet(data!, out var res);
if (isCold)
{
res ??= (Control)Activator.CreateInstance(type)!;
_controlCache[data!] = res;
_controlCache.AddOrUpdate(data!, res);
}
res.DataContext = data;
res!.DataContext = data;
return res;
}