diff --git a/GFramework.Game.Abstractions/enums/UiTransitionPolicy.cs b/GFramework.Game.Abstractions/enums/UiTransitionPolicy.cs
index b902d63..cf76e69 100644
--- a/GFramework.Game.Abstractions/enums/UiTransitionPolicy.cs
+++ b/GFramework.Game.Abstractions/enums/UiTransitionPolicy.cs
@@ -7,7 +7,7 @@
public enum UiTransitionPolicy
{
///
- /// 独占显示(下层页面 Pause + Hide)
+ /// 独占显示(下层页面 Pause + Suspend)
///
Exclusive,
diff --git a/GFramework.Game.Abstractions/ui/IUiFactory.cs b/GFramework.Game.Abstractions/ui/IUiFactory.cs
index ad73996..665451f 100644
--- a/GFramework.Game.Abstractions/ui/IUiFactory.cs
+++ b/GFramework.Game.Abstractions/ui/IUiFactory.cs
@@ -11,75 +11,6 @@ public interface IUiFactory : IContextUtility
/// 创建或获取UI页面实例
///
/// UI标识键
- /// 实例管理策略
/// UI页面实例
- IUiPageBehavior GetOrCreate(string uiKey, UiInstancePolicy policy = UiInstancePolicy.AlwaysCreate);
-
- ///
- /// 仅创建新实例(不使用缓存)
- ///
IUiPageBehavior Create(string uiKey);
-
- ///
- /// 预加载UI资源到缓存池
- ///
- /// UI标识键
- /// 预加载数量,默认1个
- void Preload(string uiKey, int count = 1);
-
- ///
- /// 批量预加载
- ///
- void PreloadBatch(params string[] uiKeys);
-
- ///
- /// 回收实例到缓存池
- ///
- /// 要回收的页面实例
- void Recycle(IUiPageBehavior page);
-
- ///
- /// 清理指定UI的缓存实例
- ///
- void ClearCache(string uiKey);
-
- ///
- /// 清理所有缓存
- ///
- void ClearAllCache();
-
- ///
- /// 检查是否有缓存的实例
- ///
- 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/IUiRouter.cs b/GFramework.Game.Abstractions/ui/IUiRouter.cs
index c8a07db..14b3556 100644
--- a/GFramework.Game.Abstractions/ui/IUiRouter.cs
+++ b/GFramework.Game.Abstractions/ui/IUiRouter.cs
@@ -25,9 +25,7 @@ public interface IUiRouter : ISystem
/// UI界面的唯一标识符
/// 进入界面的参数,可为空
/// 界面切换策略,默认为Exclusive(独占)
- /// 实例管理策略,默认为Reuse(复用)
- void Push(string uiKey, IUiPageEnterParam? param = null, UiTransitionPolicy policy = UiTransitionPolicy.Exclusive,
- UiInstancePolicy instancePolicy = UiInstancePolicy.Reuse);
+ void Push(string uiKey, IUiPageEnterParam? param = null, UiTransitionPolicy policy = UiTransitionPolicy.Exclusive);
///
@@ -54,13 +52,11 @@ public interface IUiRouter : ISystem
/// 页面进入参数,可为空
/// 弹出页面时的销毁策略,默认为销毁
/// 推入页面时的过渡策略,默认为独占
- /// 实例管理策略
public void Replace(
string uiKey,
IUiPageEnterParam? param = null,
UiPopPolicy popPolicy = UiPopPolicy.Destroy,
- UiTransitionPolicy pushPolicy = UiTransitionPolicy.Exclusive,
- UiInstancePolicy instancePolicy = UiInstancePolicy.Reuse);
+ UiTransitionPolicy pushPolicy = UiTransitionPolicy.Exclusive);
///
/// 替换当前所有页面为已存在的页面(基于实例)
@@ -146,12 +142,10 @@ public interface IUiRouter : ISystem
/// 要显示的UI页面的唯一标识符
/// UI显示的层级,例如 Overlay、Modal 或 Toast
/// 可选参数,用于传递给UI页面的初始化数据
- /// UI实例策略,默认为复用已存在的实例
void Show(
string uiKey,
UiLayer layer,
- IUiPageEnterParam? param = null,
- UiInstancePolicy instancePolicy = UiInstancePolicy.Reuse);
+ IUiPageEnterParam? param = null);
///
/// 在指定层级显示UI(Overlay / Modal / Toast等)
diff --git a/GFramework.Game.Abstractions/ui/UiInstancePolicy.cs b/GFramework.Game.Abstractions/ui/UiInstancePolicy.cs
deleted file mode 100644
index 1fbf7ff..0000000
--- a/GFramework.Game.Abstractions/ui/UiInstancePolicy.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-namespace GFramework.Game.Abstractions.ui;
-
-///
-/// UI页面实例管理策略(控制实例的生命周期)
-///
-public enum UiInstancePolicy
-{
- ///
- /// 总是创建新实例
- ///
- AlwaysCreate,
-
- ///
- /// 复用已存在的实例(如果有)
- ///
- Reuse,
-
- ///
- /// 从预加载池中获取或创建
- ///
- Pooled
-}
\ No newline at end of file
diff --git a/GFramework.Game.Abstractions/ui/UiPopPolicy.cs b/GFramework.Game.Abstractions/ui/UiPopPolicy.cs
index 6ba7e8c..9930d48 100644
--- a/GFramework.Game.Abstractions/ui/UiPopPolicy.cs
+++ b/GFramework.Game.Abstractions/ui/UiPopPolicy.cs
@@ -11,7 +11,7 @@ public enum UiPopPolicy
Destroy,
///
- /// 隐藏但保留实例(下次Push可复用)
+ /// 可恢复
///
- Cache
+ Suspend
}
\ No newline at end of file
diff --git a/GFramework.Game/ui/UiRouterBase.cs b/GFramework.Game/ui/UiRouterBase.cs
index ee6ce1d..e6ec761 100644
--- a/GFramework.Game/ui/UiRouterBase.cs
+++ b/GFramework.Game/ui/UiRouterBase.cs
@@ -76,10 +76,8 @@ public abstract class UiRouterBase : AbstractSystem, IUiRouter
/// UI界面的唯一标识符
/// 进入界面的参数,可为空
/// 界面切换策略,默认为Exclusive(独占)
- /// 实例管理策略,默认为Reuse(复用)
public void Push(string uiKey, IUiPageEnterParam? param = null,
- UiTransitionPolicy policy = UiTransitionPolicy.Exclusive,
- UiInstancePolicy instancePolicy = UiInstancePolicy.Reuse)
+ UiTransitionPolicy policy = UiTransitionPolicy.Exclusive)
{
if (IsTop(uiKey))
{
@@ -90,12 +88,12 @@ public abstract class UiRouterBase : AbstractSystem, IUiRouter
var @event = CreateEvent(uiKey, UiTransitionType.Push, policy, param);
Log.Debug(
- "Push UI Page: key={0}, policy={1}, instancePolicy={2}, stackBefore={3}",
- uiKey, policy, instancePolicy, _stack.Count
+ "Push UI Page: key={0}, policy={1}, stackBefore={2}",
+ uiKey, policy, _stack.Count
);
BeforeChange(@event);
- DoPushPageInternal(uiKey, param, policy, instancePolicy);
+ DoPushPageInternal(uiKey, param, policy);
AfterChange(@event);
}
@@ -176,18 +174,16 @@ public abstract class UiRouterBase : AbstractSystem, IUiRouter
/// 页面进入参数,可为空
/// 弹出页面时的销毁策略,默认为销毁
/// 推入页面时的过渡策略,默认为独占
- /// 实例管理策略
public void Replace(
string uiKey,
IUiPageEnterParam? param = null,
UiPopPolicy popPolicy = UiPopPolicy.Destroy,
- UiTransitionPolicy pushPolicy = UiTransitionPolicy.Exclusive,
- UiInstancePolicy instancePolicy = UiInstancePolicy.Reuse)
+ UiTransitionPolicy pushPolicy = UiTransitionPolicy.Exclusive)
{
var @event = CreateEvent(uiKey, UiTransitionType.Replace, pushPolicy, param);
Log.Debug(
- "Replace UI Stack with page: key={0}, popPolicy={1}, pushPolicy={2}, instancePolicy={3}",
- uiKey, popPolicy, pushPolicy, instancePolicy
+ "Replace UI Stack with page: key={0}, popPolicy={1}, pushPolicy={2}",
+ uiKey, popPolicy, pushPolicy
);
BeforeChange(@event);
@@ -196,7 +192,7 @@ public abstract class UiRouterBase : AbstractSystem, IUiRouter
DoClearInternal(popPolicy);
// 使用工厂的增强方法获取实例
- var page = _factory.GetOrCreate(uiKey, instancePolicy);
+ var page = _factory.Create(uiKey);
Log.Debug("Get/Create UI Page instance for Replace: {0}", page.GetType().Name);
DoPushPageInternal(page, param, pushPolicy);
@@ -366,8 +362,7 @@ public abstract class UiRouterBase : AbstractSystem, IUiRouter
///
/// 执行Push页面的核心逻辑(基于 uiKey)
///
- private void DoPushPageInternal(string uiKey, IUiPageEnterParam? param, UiTransitionPolicy policy,
- UiInstancePolicy instancePolicy)
+ private void DoPushPageInternal(string uiKey, IUiPageEnterParam? param, UiTransitionPolicy policy)
{
// 执行进入守卫
if (!ExecuteEnterGuardsAsync(uiKey, param).GetAwaiter().GetResult())
@@ -377,7 +372,7 @@ public abstract class UiRouterBase : AbstractSystem, IUiRouter
}
// 使用工厂的增强方法获取实例
- var page = _factory.GetOrCreate(uiKey, instancePolicy);
+ var page = _factory.Create(uiKey);
Log.Debug("Get/Create UI Page instance: {0}", page.GetType().Name);
DoPushPageInternal(page, param, policy);
@@ -397,7 +392,7 @@ public abstract class UiRouterBase : AbstractSystem, IUiRouter
if (policy == UiTransitionPolicy.Exclusive)
{
- Log.Debug("Hide current page (Exclusive): {0}", current.View.GetType().Name);
+ Log.Debug("Suspend current page (Exclusive): {0}", current.View.GetType().Name);
current.OnHide();
}
}
@@ -428,37 +423,26 @@ public abstract class UiRouterBase : AbstractSystem, IUiRouter
return;
var top = _stack.Pop();
+
Log.Debug(
"Pop UI Page internal: {0}, policy={1}, stackAfterPop={2}",
top.GetType().Name, policy, _stack.Count
);
- top.OnExit();
-
if (policy == UiPopPolicy.Destroy)
{
- Log.Debug("Destroy UI Page: {0}", top.GetType().Name);
+ top.OnExit();
_uiRoot.RemoveUiPage(top);
- // 不回收,直接销毁
}
- else // UiPopPolicy.Cache
+ else // Suspend
{
- Log.Debug("Cache UI Page: {0}", top.GetType().Name);
- _uiRoot.RemoveUiPage(top);
- _factory.Recycle(top); // 回收到池中
+ top.OnHide();
}
- if (_stack.Count > 0)
- {
- var next = _stack.Peek();
- Log.Debug("Resume & Show page: {0}", next.GetType().Name);
- next.OnResume();
- next.OnShow();
- }
- else
- {
- Log.Debug("UI stack is now empty");
- }
+ if (_stack.Count <= 0) return;
+ var next = _stack.Peek();
+ next.OnResume();
+ next.OnShow();
}
///
@@ -481,8 +465,7 @@ public abstract class UiRouterBase : AbstractSystem, IUiRouter
public void Show(
string uiKey,
UiLayer layer,
- IUiPageEnterParam? param = null,
- UiInstancePolicy instancePolicy = UiInstancePolicy.Reuse)
+ IUiPageEnterParam? param = null)
{
if (layer == UiLayer.Page) throw new ArgumentException("Use Push() for Page layer");
@@ -502,7 +485,7 @@ public abstract class UiRouterBase : AbstractSystem, IUiRouter
}
// 获取或创建实例
- var page = _factory.GetOrCreate(uiKey, instancePolicy);
+ var page = _factory.Create(uiKey);
layerDict[uiKey] = page;
// 添加到UiRoot,传入层级Z-order
@@ -580,14 +563,11 @@ public abstract class UiRouterBase : AbstractSystem, IUiRouter
{
_uiRoot.RemoveUiPage(page);
layerDict.Remove(uiKey);
- Log.Debug("Hide & Destroy UI from layer: {0}, layer={1}", uiKey, layer);
+ Log.Debug("Suspend & Destroy UI from layer: {0}, layer={1}", uiKey, layer);
}
else
{
- _uiRoot.RemoveUiPage(page);
- _factory.Recycle(page);
- layerDict.Remove(uiKey);
- Log.Debug("Hide & Cache UI from layer: {0}, layer={1}", uiKey, layer);
+ Log.Debug("Suspend & Suspend UI from layer: {0}, layer={1}", uiKey, layer);
}
}
diff --git a/GFramework.Godot/ui/CanvasItemUiPageBehavior.cs b/GFramework.Godot/ui/CanvasItemUiPageBehavior.cs
index cc22bc3..e6a9e1d 100644
--- a/GFramework.Godot/ui/CanvasItemUiPageBehavior.cs
+++ b/GFramework.Godot/ui/CanvasItemUiPageBehavior.cs
@@ -68,6 +68,11 @@ public class CanvasItemUiPageBehavior(T owner, string key) : IUiPageBehavior
///
public void OnResume()
{
+ if (owner.IsInvalidNode())
+ {
+ return;
+ }
+
_page?.OnResume();
// 恢复节点的处理、物理处理和输入处理
diff --git a/GFramework.Godot/ui/GodotUiFactory.cs b/GFramework.Godot/ui/GodotUiFactory.cs
index 5708532..5aeaf2a 100644
--- a/GFramework.Godot/ui/GodotUiFactory.cs
+++ b/GFramework.Godot/ui/GodotUiFactory.cs
@@ -2,435 +2,62 @@ using GFramework.Core.Abstractions.logging;
using GFramework.Core.extensions;
using GFramework.Core.logging;
using GFramework.Core.utility;
-using GFramework.Game.Abstractions.enums;
using GFramework.Game.Abstractions.ui;
-using GFramework.Godot.extensions;
-using Godot;
namespace GFramework.Godot.ui;
///
-/// Godot UI工厂类,用于创建UI页面实例
-/// 继承自AbstractContextUtility并实现IUiFactory接口
+/// Godot UI工厂类,用于创建UI页面实例。
+/// 继承自AbstractContextUtility并实现IUiFactory接口。
///
public class GodotUiFactory : AbstractContextUtility, IUiFactory
{
- private static readonly ILogger Log = LoggerFactoryResolver.Provider.CreateLogger("GodotUiFactory");
+ ///
+ /// 日志记录器,用于记录调试信息。
+ ///
+ private static readonly ILogger Log =
+ LoggerFactoryResolver.Provider.CreateLogger("GodotUiFactory");
///
- /// LFU访问计数:instance -> 访问次数
+ /// UI注册表,用于管理UI场景资源。
///
- private readonly Dictionary _accessCount = new();
-
- ///
- /// LRU访问时间队列:uiKey -> 按访问时间排序的实例列表
- ///
- private readonly Dictionary> _accessTimeQueue = new();
-
- ///
- /// 追踪所有创建的实例(用于清理)
- ///
- private readonly Dictionary> _allInstances = new();
-
- ///
- /// 缓存配置:uiKey -> 配置
- ///
- private readonly Dictionary _cacheConfigs = new();
-
- ///
- /// 缓存池:uiKey -> 实例队列
- ///
- private readonly Dictionary> _cachedInstances = new();
-
- ///
- /// 缓存统计:uiKey -> 统计信息
- ///
- private readonly Dictionary _cacheStatistics = new();
-
private IGodotUiRegistry _registry = null!;
///
- /// 创建或获取UI页面实例
- ///
- public IUiPageBehavior GetOrCreate(string uiKey, UiInstancePolicy policy = UiInstancePolicy.AlwaysCreate)
- {
- return policy switch
- {
- UiInstancePolicy.Reuse => GetCachedOrCreate(uiKey),
- UiInstancePolicy.Pooled => GetFromPoolOrCreate(uiKey),
- _ => Create(uiKey)
- };
- }
-
- ///
- /// 仅创建新实例
+ /// 根据指定的UI键创建UI页面实例。
///
+ /// UI页面的唯一标识符。
+ /// 返回创建的UI页面行为实例。
+ ///
+ /// 当UI场景未实现IUiPageBehaviorProvider接口时抛出异常。
+ ///
public IUiPageBehavior Create(string uiKey)
{
+ // 从注册表中获取指定UI键对应的场景
var scene = _registry.Get(uiKey);
+
+ // 实例化场景节点
var node = scene.Instantiate();
+ // 检查节点是否实现了IUiPageBehaviorProvider接口
if (node is not IUiPageBehaviorProvider provider)
- throw new InvalidCastException($"UI scene {uiKey} must implement IUiPageBehaviorProvider");
+ throw new InvalidCastException(
+ $"UI scene {uiKey} must implement IUiPageBehaviorProvider");
+ // 获取页面行为实例
var page = provider.GetPage();
- // 追踪实例
- if (!_allInstances.ContainsKey(uiKey))
- _allInstances[uiKey] = new HashSet();
- _allInstances[uiKey].Add(page);
-
- Log.Debug("Created new UI instance: {0}", uiKey);
+ // 记录调试日志
+ Log.Debug("Created UI instance: {0}", uiKey);
return page;
}
///
- /// 预加载UI资源
+ /// 初始化方法,在对象初始化时调用。
+ /// 获取并设置UI注册表实例。
///
- public void Preload(string uiKey, int count = 1)
- {
- Log.Debug("Preloading UI: {0}, count={1}", uiKey, count);
-
- if (!_cachedInstances.ContainsKey(uiKey))
- _cachedInstances[uiKey] = new Queue();
-
- var queue = _cachedInstances[uiKey];
-
- for (var i = 0; i < count; i++)
- {
- var instance = Create(uiKey);
- // 预加载的实例初始状态为隐藏
- instance.OnHide();
- queue.Enqueue(instance);
- }
-
- Log.Debug("Preloaded {0} instances of {1}", count, uiKey);
- }
-
- ///
- /// 批量预加载
- ///
- public void PreloadBatch(params string[] uiKeys)
- {
- foreach (var uiKey in uiKeys) Preload(uiKey);
- }
-
- ///
- /// 回收实例到缓存池
- ///
- public void Recycle(IUiPageBehavior page)
- {
- var uiKey = page.Key;
-
- if (!_cachedInstances.ContainsKey(uiKey))
- _cachedInstances[uiKey] = new Queue();
-
- // 确保实例处于隐藏状态
- 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的缓存
- ///
- public void ClearCache(string uiKey)
- {
- if (!_cachedInstances.TryGetValue(uiKey, out var queue))
- return;
-
- var count = queue.Count;
- while (queue.Count > 0)
- {
- var instance = queue.Dequeue();
- DestroyInstance(instance);
- }
-
- _cachedInstances.Remove(uiKey);
- Log.Debug("Cleared cache for UI: {0}, destroyed {1} instances", uiKey, count);
- }
-
- ///
- /// 清理所有缓存
- ///
- public void ClearAllCache()
- {
- foreach (var uiKey in _cachedInstances.Keys) ClearCache(uiKey);
-
- Log.Debug("Cleared all UI caches");
- }
-
- ///
- /// 检查是否有缓存的实例
- ///
- public bool HasCached(string uiKey)
- {
- return _cachedInstances.TryGetValue(uiKey, out var queue) && queue.Count > 0;
- }
-
protected override void OnInit()
{
_registry = this.GetUtility()!;
}
-
- ///
- /// 缓存统计信息实现类
- ///
- private sealed 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; }
- }
-
- #region Private Methods
-
- ///
- /// 获取缓存实例或创建新实例(Reuse策略)
- ///
- private IUiPageBehavior GetCachedOrCreate(string uiKey)
- {
- // 优先从缓存池获取
- 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);
- }
-
- ///
- /// 从池中获取或创建(Pooled策略)
- /// 如果池为空,自动创建并填充
- ///
- private IUiPageBehavior GetFromPoolOrCreate(string uiKey)
- {
- // 如果池为空,先预加载一个
- if (HasCached(uiKey)) return GetCachedOrCreate(uiKey);
- Log.Debug("Pool empty, preloading instance: {0}", uiKey);
- Preload(uiKey);
-
- return GetCachedOrCreate(uiKey);
- }
-
- ///
- /// 销毁实例
- ///
- private void DestroyInstance(IUiPageBehavior page)
- {
- var uiKey = page.Key;
-
- // 从追踪列表移除
- if (_allInstances.TryGetValue(uiKey, out var set)) 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) 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) return;
- var toEvict = currentSize - config.MaxCacheSize;
-
- for (var 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