From ad061bba4642ee4ea0a83f802837768f7c600dab Mon Sep 17 00:00:00 2001
From: GeWuYou <95328647+GeWuYou@users.noreply.github.com>
Date: Tue, 20 Jan 2026 10:24:23 +0800
Subject: [PATCH] =?UTF-8?q?feat(ui):=20=E6=B7=BB=E5=8A=A0UI=E7=BC=93?=
=?UTF-8?q?=E5=AD=98=E7=BB=9F=E8=AE=A1=E5=92=8C=E8=B7=AF=E7=94=B1=E5=AE=88?=
=?UTF-8?q?=E5=8D=AB=E5=8A=9F=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 新增IUiCacheStatistics接口用于UI缓存统计信息
- 为IUiFactory添加缓存策略管理和统计信息获取功能
- 将IUiRouter中的层级管理改为路由守卫功能
- 实现路由守卫的注册、移除和执行逻辑
- 添加缓存配置管理支持
- [skip ci]
---
GFramework.Game.Abstractions/ui/IUiFactory.cs | 65 ++++-
.../ui/IUiRouteGuard.cs | 36 +++
GFramework.Game.Abstractions/ui/IUiRouter.cs | 52 +---
.../ui/UiCacheConfig.cs | 77 ++++++
GFramework.Game/ui/UiRouterBase.cs | 165 +++++++++++-
GFramework.Godot/ui/GodotUiFactory.cs | 250 +++++++++++++++++-
6 files changed, 592 insertions(+), 53 deletions(-)
create mode 100644 GFramework.Game.Abstractions/ui/IUiRouteGuard.cs
create mode 100644 GFramework.Game.Abstractions/ui/UiCacheConfig.cs
diff --git a/GFramework.Game.Abstractions/ui/IUiFactory.cs b/GFramework.Game.Abstractions/ui/IUiFactory.cs
index b7e1199..70bcf56 100644
--- a/GFramework.Game.Abstractions/ui/IUiFactory.cs
+++ b/GFramework.Game.Abstractions/ui/IUiFactory.cs
@@ -1,7 +1,40 @@
-using GFramework.Core.Abstractions.utility;
+using System;
+using System.Collections.Generic;
+using GFramework.Core.Abstractions.utility;
namespace GFramework.Game.Abstractions.ui;
+///
+/// UI缓存统计信息接口
+///
+public interface IUiCacheStatistics
+{
+ ///
+ /// 缓存总数
+ ///
+ int CacheSize { get; }
+
+ ///
+ /// 缓存命中次数
+ ///
+ int HitCount { get; }
+
+ ///
+ /// 缓存未命中次数
+ ///
+ int MissCount { get; }
+
+ ///
+ /// 命中率
+ ///
+ double HitRate { get; }
+
+ ///
+ /// 最近访问时间
+ ///
+ DateTime? LastAccessTime { get; }
+}
+
///
/// UI工厂接口,用于创建UI页面实例
///
@@ -52,4 +85,34 @@ public interface IUiFactory : IContextUtility
/// 检查是否有缓存的实例
///
bool HasCached(string uiKey);
+
+ #region 缓存策略管理
+
+ ///
+ /// 获取UI的缓存配置
+ ///
+ /// UI标识符
+ /// 缓存配置,如果未设置则返回默认配置
+ UiCacheConfig GetCacheConfig(string uiKey);
+
+ ///
+ /// 设置UI的缓存配置
+ ///
+ /// UI标识符
+ /// 缓存配置
+ void SetCacheConfig(string uiKey, UiCacheConfig config);
+
+ ///
+ /// 移除UI的缓存配置,恢复默认配置
+ ///
+ /// UI标识符
+ void RemoveCacheConfig(string uiKey);
+
+ ///
+ /// 获取所有UI的缓存统计信息
+ ///
+ /// 缓存统计字典
+ IDictionary GetCacheStatistics();
+
+ #endregion
}
\ No newline at end of file
diff --git a/GFramework.Game.Abstractions/ui/IUiRouteGuard.cs b/GFramework.Game.Abstractions/ui/IUiRouteGuard.cs
new file mode 100644
index 0000000..bdff7b8
--- /dev/null
+++ b/GFramework.Game.Abstractions/ui/IUiRouteGuard.cs
@@ -0,0 +1,36 @@
+using System.Threading.Tasks;
+
+namespace GFramework.Game.Abstractions.ui;
+
+///
+/// UI路由守卫接口
+/// 用于拦截和处理UI路由切换,实现业务逻辑解耦
+///
+public interface IUiRouteGuard
+{
+ ///
+ /// 守卫优先级,数值越小越先执行
+ ///
+ int Priority { get; }
+
+ ///
+ /// 是否可中断后续守卫
+ /// 如果返回 true,当该守卫返回 false 时,将停止执行后续守卫
+ ///
+ bool CanInterrupt { get; }
+
+ ///
+ /// 进入UI前的检查
+ ///
+ /// 目标UI标识符
+ /// 进入参数
+ /// true表示允许进入,false表示拦截
+ Task CanEnterAsync(string uiKey, IUiPageEnterParam? param);
+
+ ///
+ /// 离开UI前的检查
+ ///
+ /// 当前UI标识符
+ /// true表示允许离开,false表示拦截
+ Task CanLeaveAsync(string uiKey);
+}
diff --git a/GFramework.Game.Abstractions/ui/IUiRouter.cs b/GFramework.Game.Abstractions/ui/IUiRouter.cs
index c12172c..08bd15d 100644
--- a/GFramework.Game.Abstractions/ui/IUiRouter.cs
+++ b/GFramework.Game.Abstractions/ui/IUiRouter.cs
@@ -121,57 +121,25 @@ public interface IUiRouter : ISystem
///
bool Contains(string uiKey);
- #region 层级管理
+ #region 路由守卫
///
- /// 在指定层级显示UI(非栈管理)
+ /// 注册路由守卫
///
- /// UI标识符
- /// UI层级
- /// 进入参数
- /// 实例策略
- void Show(
- string uiKey,
- UiLayer layer,
- IUiPageEnterParam? param = null,
- UiInstancePolicy instancePolicy = UiInstancePolicy.Reuse);
+ /// 守卫实例
+ void AddGuard(IUiRouteGuard guard);
///
- /// 在指定层级显示UI(基于实例)
+ /// 移除路由守卫
///
- /// UI页面实例
- /// UI层级
- void Show(IUiPageBehavior page, UiLayer layer);
+ /// 守卫实例
+ void RemoveGuard(IUiRouteGuard guard);
///
- /// 隐藏指定层级的UI
+ /// 注册路由守卫(泛型方法)
///
- /// UI标识符
- /// UI层级
- /// 是否销毁实例
- void Hide(string uiKey, UiLayer layer, bool destroy = false);
-
- ///
- /// 清空指定层级的所有UI
- ///
- /// UI层级
- /// 是否销毁实例
- void ClearLayer(UiLayer layer, bool destroy = false);
-
- ///
- /// 获取指定层级的UI实例
- ///
- /// UI标识符
- /// UI层级
- /// UI实例,不存在则返回null
- IUiPageBehavior? GetFromLayer(string uiKey, UiLayer layer);
-
- ///
- /// 判断指定层级是否有UI显示
- ///
- /// UI层级
- /// 是否有UI显示
- bool HasVisibleInLayer(UiLayer layer);
+ /// 守卫类型,必须实现 IUiRouteGuard 且有无参构造函数
+ void AddGuard() where T : IUiRouteGuard, new();
#endregion
}
\ No newline at end of file
diff --git a/GFramework.Game.Abstractions/ui/UiCacheConfig.cs b/GFramework.Game.Abstractions/ui/UiCacheConfig.cs
new file mode 100644
index 0000000..c5e14ca
--- /dev/null
+++ b/GFramework.Game.Abstractions/ui/UiCacheConfig.cs
@@ -0,0 +1,77 @@
+using System;
+
+namespace GFramework.Game.Abstractions.ui;
+
+///
+/// UI缓存配置
+/// 用于配置UI实例的缓存行为
+///
+public class UiCacheConfig
+{
+ ///
+ /// 最大缓存数量
+ ///
+ public int MaxCacheSize { get; set; } = 10;
+
+ ///
+ /// 缓存淘汰策略
+ ///
+ public CacheEvictionPolicy EvictionPolicy { get; set; } = CacheEvictionPolicy.LRU;
+
+ ///
+ /// 访问后过期时间(可选,null 表示不启用)
+ ///
+ public TimeSpan? ExpireAfterAccess { get; set; } = null;
+
+ ///
+ /// 创建默认配置(LRU 策略,最大 10 个实例)
+ ///
+ public static UiCacheConfig Default => new UiCacheConfig
+ {
+ MaxCacheSize = 10,
+ EvictionPolicy = CacheEvictionPolicy.LRU,
+ ExpireAfterAccess = null
+ };
+
+ ///
+ /// 创建 LRU 策略配置
+ ///
+ /// 最大缓存数量
+ /// 访问后过期时间
+ public static UiCacheConfig Lru(int maxSize = 10, TimeSpan? expireAfter = null)
+ => new UiCacheConfig
+ {
+ MaxCacheSize = maxSize,
+ EvictionPolicy = CacheEvictionPolicy.LRU,
+ ExpireAfterAccess = expireAfter
+ };
+
+ ///
+ /// 创建 LFU 策略配置
+ ///
+ /// 最大缓存数量
+ /// 访问后过期时间
+ public static UiCacheConfig Lfu(int maxSize = 10, TimeSpan? expireAfter = null)
+ => new UiCacheConfig
+ {
+ MaxCacheSize = maxSize,
+ EvictionPolicy = CacheEvictionPolicy.LFU,
+ ExpireAfterAccess = expireAfter
+ };
+}
+
+///
+/// 缓存淘汰策略枚举
+///
+public enum CacheEvictionPolicy
+{
+ ///
+ /// 最近最少使用
+ ///
+ LRU,
+
+ ///
+ /// 最少使用频率
+ ///
+ LFU
+}
diff --git a/GFramework.Game/ui/UiRouterBase.cs b/GFramework.Game/ui/UiRouterBase.cs
index 4ffd2fe..70b5a79 100644
--- a/GFramework.Game/ui/UiRouterBase.cs
+++ b/GFramework.Game/ui/UiRouterBase.cs
@@ -37,6 +37,11 @@ public abstract class UiRouterBase : AbstractSystem, IUiRouter
private IUiRoot _uiRoot = null!;
+ ///
+ /// 路由守卫列表
+ ///
+ private readonly List _guards = new();
+
///
/// 注册UI切换处理器
///
@@ -92,13 +97,7 @@ public abstract class UiRouterBase : AbstractSystem, IUiRouter
);
BeforeChange(@event);
-
- // 使用工厂的增强方法获取实例
- var page = _factory.GetOrCreate(uiKey, instancePolicy);
- Log.Debug("Get/Create UI Page instance: {0}", page.GetType().Name);
-
- DoPushPageInternal(page, param, policy);
-
+ DoPushPageInternal(uiKey, param, policy, instancePolicy, animationPolicy);
AfterChange(@event);
}
@@ -149,6 +148,15 @@ public abstract class UiRouterBase : AbstractSystem, IUiRouter
return;
}
+ var topUiKey = _stack.Peek().Key;
+
+ // 执行离开守卫
+ if (!ExecuteLeaveGuardsAsync(topUiKey).GetAwaiter().GetResult())
+ {
+ Log.Warn("Pop blocked by guard: {0}", topUiKey);
+ return;
+ }
+
var nextUiKey = _stack.Count > 1
? _stack.ElementAt(1).Key // 使用 Key 而不是 View.GetType().Name
: throw new InvalidOperationException("Stack is empty");
@@ -362,8 +370,27 @@ public abstract class UiRouterBase : AbstractSystem, IUiRouter
}
///
- /// 执行Push页面的核心逻辑(统一处理)
- /// 这个方法同时服务于工厂创建和已存在页面两种情况
+ /// 执行Push页面的核心逻辑(基于 uiKey)
+ ///
+ private void DoPushPageInternal(string uiKey, IUiPageEnterParam? param, UiTransitionPolicy policy,
+ UiInstancePolicy instancePolicy, UiAnimationPolicy? animationPolicy)
+ {
+ // 执行进入守卫
+ if (!ExecuteEnterGuardsAsync(uiKey, param).GetAwaiter().GetResult())
+ {
+ Log.Warn("Push blocked by guard: {0}", uiKey);
+ return;
+ }
+
+ // 使用工厂的增强方法获取实例
+ var page = _factory.GetOrCreate(uiKey, instancePolicy);
+ Log.Debug("Get/Create UI Page instance: {0}", page.GetType().Name);
+
+ DoPushPageInternal(page, param, policy);
+ }
+
+ ///
+ /// 执行Push页面的核心逻辑(基于 page)
///
private void DoPushPageInternal(IUiPageBehavior page, IUiPageEnterParam? param, UiTransitionPolicy policy)
{
@@ -582,4 +609,124 @@ public abstract class UiRouterBase : AbstractSystem, IUiRouter
}
#endregion
+
+ #region 路由守卫
+
+
+///
+/// 注册路由守卫
+///
+public void AddGuard(IUiRouteGuard guard)
+{
+ ArgumentNullException.ThrowIfNull(guard);
+
+ if (_guards.Contains(guard))
+ {
+ Log.Debug("Guard already registered: {0}", guard.GetType().Name);
+ return;
+ }
+
+ _guards.Add(guard);
+ // 按优先级排序
+ _guards.Sort((a, b) => a.Priority.CompareTo(b.Priority));
+ Log.Debug("Guard registered: {0}, Priority={1}", guard.GetType().Name, guard.Priority);
+}
+
+///
+/// 移除路由守卫
+///
+public void RemoveGuard(IUiRouteGuard guard)
+{
+ ArgumentNullException.ThrowIfNull(guard);
+
+ if (_guards.Remove(guard))
+ {
+ Log.Debug("Guard removed: {0}", guard.GetType().Name);
+ }
+}
+
+///
+/// 注册路由守卫(泛型方法)
+///
+public void AddGuard() where T : IUiRouteGuard, new()
+{
+ var guard = new T();
+ AddGuard(guard);
+}
+
+///
+/// 执行进入守卫
+///
+private async Task ExecuteEnterGuardsAsync(string uiKey, IUiPageEnterParam? param)
+{
+ foreach (var guard in _guards)
+ {
+ try
+ {
+ Log.Debug("Executing enter guard: {0} for {1}", guard.GetType().Name, uiKey);
+ var canEnter = await guard.CanEnterAsync(uiKey, param);
+
+ if (!canEnter)
+ {
+ Log.Debug("Enter guard blocked: {0}", guard.GetType().Name);
+ return false;
+ }
+
+ if (guard.CanInterrupt)
+ {
+ Log.Debug("Enter guard {0} passed, can interrupt = true", guard.GetType().Name);
+ return true;
+ }
+ }
+ catch (Exception ex)
+ {
+ Log.Error("Enter guard {0} failed: {1}", guard.GetType().Name, ex.Message);
+ if (guard.CanInterrupt)
+ {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+///
+/// 执行离开守卫
+///
+private async Task ExecuteLeaveGuardsAsync(string uiKey)
+{
+ foreach (var guard in _guards)
+ {
+ try
+ {
+ Log.Debug("Executing leave guard: {0} for {1}", guard.GetType().Name, uiKey);
+ var canLeave = await guard.CanLeaveAsync(uiKey);
+
+ if (!canLeave)
+ {
+ Log.Debug("Leave guard blocked: {0}", guard.GetType().Name);
+ return false;
+ }
+
+ if (guard.CanInterrupt)
+ {
+ Log.Debug("Leave guard {0} passed, can interrupt = true", guard.GetType().Name);
+ return true;
+ }
+ }
+ catch (Exception ex)
+ {
+ Log.Error("Leave guard {0} failed: {1}", guard.GetType().Name, ex.Message);
+ if (guard.CanInterrupt)
+ {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+#endregion
}
\ No newline at end of file
diff --git a/GFramework.Godot/ui/GodotUiFactory.cs b/GFramework.Godot/ui/GodotUiFactory.cs
index a5f57dd..db6c568 100644
--- a/GFramework.Godot/ui/GodotUiFactory.cs
+++ b/GFramework.Godot/ui/GodotUiFactory.cs
@@ -1,4 +1,5 @@
-using GFramework.Core.Abstractions.logging;
+using System;
+using GFramework.Core.Abstractions.logging;
using GFramework.Core.extensions;
using GFramework.Core.logging;
using GFramework.Core.utility;
@@ -14,6 +15,18 @@ namespace GFramework.Godot.ui;
///
public class GodotUiFactory : AbstractContextUtility, IUiFactory
{
+ ///
+ /// 缓存统计信息实现类
+ ///
+ private class CacheStatisticsInfo : IUiCacheStatistics
+ {
+ public int CacheSize { get; set; }
+ public int HitCount { get; set; }
+ public int MissCount { get; set; }
+ public double HitRate => HitCount + MissCount > 0 ? (double)HitCount / (HitCount + MissCount) : 0;
+ public DateTime? LastAccessTime { get; set; }
+ }
+
private static readonly ILogger Log = LoggerFactoryResolver.Provider.CreateLogger("GodotUiFactory");
private IGodotUiRegistry _registry = null!;
@@ -27,6 +40,26 @@ public class GodotUiFactory : AbstractContextUtility, IUiFactory
/// 追踪所有创建的实例(用于清理)
///
private readonly Dictionary> _allInstances = new();
+
+ ///
+ /// 缓存配置:uiKey -> 配置
+ ///
+ private readonly Dictionary _cacheConfigs = new();
+
+ ///
+ /// 缓存统计:uiKey -> 统计信息
+ ///
+ private readonly Dictionary _cacheStatistics = new();
+
+ ///
+ /// LRU访问时间队列:uiKey -> 按访问时间排序的实例列表
+ ///
+ private readonly Dictionary> _accessTimeQueue = new();
+
+ ///
+ /// LFU访问计数:instance -> 访问次数
+ ///
+ private readonly Dictionary _accessCount = new();
///
/// 创建或获取UI页面实例
@@ -110,10 +143,63 @@ public class GodotUiFactory : AbstractContextUtility, IUiFactory
// 确保实例处于隐藏状态
page.OnHide();
+ // 更新统计信息
+ UpdateStatisticsOnRecycle(uiKey);
+
+ // 更新访问追踪
+ UpdateAccessTracking(uiKey, page);
+
_cachedInstances[uiKey].Enqueue(page);
Log.Debug("Recycled UI instance to pool: {0}, poolSize={1}", uiKey, _cachedInstances[uiKey].Count);
+
+ // 检查是否需要淘汰
+ CheckAndEvict(uiKey);
}
+ ///
+ /// 获取UI的缓存配置
+ ///
+ public UiCacheConfig GetCacheConfig(string uiKey)
+ {
+ return _cacheConfigs.TryGetValue(uiKey, out var config) ? config : UiCacheConfig.Default;
+ }
+
+ ///
+ /// 设置UI的缓存配置
+ ///
+ public void SetCacheConfig(string uiKey, UiCacheConfig config)
+ {
+ _cacheConfigs[uiKey] = config;
+ Log.Debug("Set cache config for UI: {0}, MaxSize={1}, Policy={2}", uiKey, config.MaxCacheSize, config.EvictionPolicy);
+
+ // 检查是否需要淘汰
+ CheckAndEvict(uiKey);
+ }
+
+ ///
+ /// 移除UI的缓存配置
+ ///
+ public void RemoveCacheConfig(string uiKey)
+ {
+ if (_cacheConfigs.Remove(uiKey))
+ {
+ Log.Debug("Removed cache config for UI: {0}", uiKey);
+ }
+ }
+
+ ///
+ /// 获取所有UI的缓存统计信息
+ ///
+ public IDictionary GetCacheStatistics()
+ {
+ var result = new Dictionary();
+ foreach (var kvp in _cacheStatistics)
+ {
+ result[kvp.Key] = kvp.Value;
+ }
+ return result;
+ }
+
///
/// 清理指定UI的缓存
///
@@ -170,11 +256,19 @@ public class GodotUiFactory : AbstractContextUtility, IUiFactory
if (_cachedInstances.TryGetValue(uiKey, out var queue) && queue.Count > 0)
{
var cached = queue.Dequeue();
+
+ // 更新统计:缓存命中
+ UpdateStatisticsOnHit(uiKey);
+
+ // 更新访问追踪
+ UpdateAccessTracking(uiKey, cached);
+
Log.Debug("Reused cached UI instance: {0}, remainingInPool={1}", uiKey, queue.Count);
return cached;
}
// 没有缓存则创建新实例
+ UpdateStatisticsOnMiss(uiKey);
Log.Debug("No cached instance, creating new: {0}", uiKey);
return Create(uiKey);
}
@@ -205,6 +299,13 @@ public class GodotUiFactory : AbstractContextUtility, IUiFactory
{
set.Remove(page);
}
+
+ // 从访问追踪移除
+ _accessCount.Remove(page);
+ if (_accessTimeQueue.TryGetValue(uiKey, out var queue))
+ {
+ queue.RemoveAll(x => x.instance == page);
+ }
// 销毁Godot节点
if (page.View is Node node)
@@ -212,6 +313,153 @@ public class GodotUiFactory : AbstractContextUtility, IUiFactory
node.QueueFreeX();
}
}
+
+ ///
+ /// 更新统计信息:回收
+ ///
+ private void UpdateStatisticsOnRecycle(string uiKey)
+ {
+ if (!_cacheStatistics.ContainsKey(uiKey))
+ _cacheStatistics[uiKey] = new CacheStatisticsInfo();
+
+ var stats = _cacheStatistics[uiKey];
+ stats.CacheSize = _cachedInstances[uiKey].Count + 1;
+ stats.LastAccessTime = DateTime.Now;
+ }
+
+ ///
+ /// 更新统计信息:命中
+ ///
+ private void UpdateStatisticsOnHit(string uiKey)
+ {
+ if (!_cacheStatistics.ContainsKey(uiKey))
+ _cacheStatistics[uiKey] = new CacheStatisticsInfo();
+
+ var stats = _cacheStatistics[uiKey];
+ stats.HitCount++;
+ stats.CacheSize = _cachedInstances[uiKey].Count;
+ stats.LastAccessTime = DateTime.Now;
+ }
+
+ ///
+ /// 更新统计信息:未命中
+ ///
+ private void UpdateStatisticsOnMiss(string uiKey)
+ {
+ if (!_cacheStatistics.ContainsKey(uiKey))
+ _cacheStatistics[uiKey] = new CacheStatisticsInfo();
+
+ var stats = _cacheStatistics[uiKey];
+ stats.MissCount++;
+ stats.CacheSize = _cachedInstances.TryGetValue(uiKey, out var queue) ? queue.Count : 0;
+ }
+
+ ///
+ /// 更新访问追踪
+ ///
+ private void UpdateAccessTracking(string uiKey, IUiPageBehavior instance)
+ {
+ var now = DateTime.Now;
+
+ // LRU: 更新访问时间队列
+ if (!_accessTimeQueue.ContainsKey(uiKey))
+ _accessTimeQueue[uiKey] = new List<(IUiPageBehavior, DateTime)>();
+
+ var timeQueue = _accessTimeQueue[uiKey];
+ timeQueue.RemoveAll(x => x.instance == instance);
+ timeQueue.Add((instance, now));
+
+ // LFU: 更新访问计数
+ _accessCount.TryGetValue(instance, out var count);
+ _accessCount[instance] = count + 1;
+ }
+
+ ///
+ /// 检查并执行淘汰
+ ///
+ private void CheckAndEvict(string uiKey)
+ {
+ var config = GetCacheConfig(uiKey);
+ var currentSize = _cachedInstances.TryGetValue(uiKey, out var queue) ? queue.Count : 0;
+
+ if (currentSize > config.MaxCacheSize)
+ {
+ var toEvict = currentSize - config.MaxCacheSize;
+
+ for (int i = 0; i < toEvict; i++)
+ {
+ if (config.EvictionPolicy == CacheEvictionPolicy.LRU)
+ EvictLRU(uiKey);
+ else
+ EvictLFU(uiKey);
+ }
+
+ Log.Debug("Evicted {0} instances for UI: {1}", toEvict, uiKey);
+ }
+ }
+
+ ///
+ /// LRU淘汰策略
+ ///
+ private void EvictLRU(string uiKey)
+ {
+ if (!_accessTimeQueue.TryGetValue(uiKey, out var timeQueue) || timeQueue.Count == 0)
+ return;
+
+ var oldest = timeQueue.OrderBy(x => x.accessTime).First();
+
+ // 从队列中移除
+ if (_cachedInstances.TryGetValue(uiKey, out var queue))
+ {
+ var tempQueue = new Queue();
+ var removed = false;
+
+ while (queue.Count > 0)
+ {
+ var item = queue.Dequeue();
+ if (!removed && item == oldest.instance)
+ {
+ DestroyInstance(item);
+ removed = true;
+ }
+ else
+ {
+ tempQueue.Enqueue(item);
+ }
+ }
+
+ // 重新填充队列
+ while (tempQueue.Count > 0)
+ queue.Enqueue(tempQueue.Dequeue());
+ }
+ }
+
+ ///
+ /// LFU淘汰策略
+ ///
+ private void EvictLFU(string uiKey)
+ {
+ if (!_cachedInstances.TryGetValue(uiKey, out var queue) || queue.Count == 0)
+ return;
+
+ // 找到访问次数最少的实例
+ IUiPageBehavior? toRemove = null;
+ var minCount = int.MaxValue;
+
+ foreach (var instance in queue)
+ {
+ if (_accessCount.TryGetValue(instance, out var count) && count < minCount)
+ {
+ minCount = count;
+ toRemove = instance;
+ }
+ }
+
+ if (toRemove != null)
+ {
+ DestroyInstance(toRemove);
+ }
+ }
#endregion
}
\ No newline at end of file