mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-05-07 08:44:29 +08:00
210 lines
6.1 KiB
C#
210 lines
6.1 KiB
C#
// Copyright (c) 2025-2026 GeWuYou
|
||
// SPDX-License-Identifier: Apache-2.0
|
||
|
||
using System.Collections.Concurrent;
|
||
|
||
namespace GFramework.Core.Resource;
|
||
|
||
/// <summary>
|
||
/// 资源缓存系统,管理已加载资源的缓存和引用计数。
|
||
/// 线程安全:所有公共方法都是线程安全的。
|
||
/// </summary>
|
||
internal sealed class ResourceCache
|
||
{
|
||
/// <summary>
|
||
/// Path 参数验证错误消息常量
|
||
/// </summary>
|
||
private const string PathCannotBeNullOrEmptyMessage = "Path cannot be null or whitespace.";
|
||
|
||
private readonly ConcurrentDictionary<string, ResourceCacheEntry> _cache = new(StringComparer.Ordinal);
|
||
#if NET9_0_OR_GREATER
|
||
// net9.0 及以上目标使用专用 Lock,以满足分析器对专用同步原语的建议。
|
||
private readonly System.Threading.Lock _lock = new();
|
||
#else
|
||
// net8.0 目标仍回退到 object 锁,以保持多目标编译兼容性。
|
||
private readonly object _lock = new();
|
||
#endif
|
||
|
||
/// <summary>
|
||
/// 获取已缓存资源的数量
|
||
/// </summary>
|
||
public int Count => _cache.Count;
|
||
|
||
/// <summary>
|
||
/// 添加资源到缓存
|
||
/// </summary>
|
||
/// <typeparam name="T">资源类型</typeparam>
|
||
/// <param name="path">资源路径</param>
|
||
/// <param name="resource">资源实例</param>
|
||
/// <returns>如果成功添加返回 true,如果已存在返回 false</returns>
|
||
public bool Add<T>(string path, T resource) where T : class
|
||
{
|
||
if (string.IsNullOrWhiteSpace(path))
|
||
throw new ArgumentException(PathCannotBeNullOrEmptyMessage, nameof(path));
|
||
|
||
ArgumentNullException.ThrowIfNull(resource);
|
||
|
||
var entry = new ResourceCacheEntry(resource, typeof(T));
|
||
return _cache.TryAdd(path, entry);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 从缓存中获取资源
|
||
/// </summary>
|
||
/// <typeparam name="T">资源类型</typeparam>
|
||
/// <param name="path">资源路径</param>
|
||
/// <returns>资源实例,如果不存在或类型不匹配返回 null</returns>
|
||
public T? Get<T>(string path) where T : class
|
||
{
|
||
if (string.IsNullOrWhiteSpace(path))
|
||
throw new ArgumentException(PathCannotBeNullOrEmptyMessage, nameof(path));
|
||
|
||
if (_cache.TryGetValue(path, out var entry))
|
||
{
|
||
lock (_lock)
|
||
{
|
||
entry.LastAccessTime = DateTime.UtcNow;
|
||
}
|
||
|
||
if (entry.Resource is T typedResource)
|
||
{
|
||
return typedResource;
|
||
}
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检查资源是否在缓存中
|
||
/// </summary>
|
||
/// <param name="path">资源路径</param>
|
||
/// <returns>如果存在返回 true,否则返回 false</returns>
|
||
public bool Contains(string path)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(path))
|
||
throw new ArgumentException(PathCannotBeNullOrEmptyMessage, nameof(path));
|
||
|
||
return _cache.ContainsKey(path);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 从缓存中移除资源
|
||
/// </summary>
|
||
/// <param name="path">资源路径</param>
|
||
/// <returns>如果成功移除返回 true,否则返回 false</returns>
|
||
public bool Remove(string path)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(path))
|
||
throw new ArgumentException(PathCannotBeNullOrEmptyMessage, nameof(path));
|
||
|
||
return _cache.TryRemove(path, out _);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 清空所有缓存
|
||
/// </summary>
|
||
public void Clear()
|
||
{
|
||
_cache.Clear();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 增加资源的引用计数
|
||
/// </summary>
|
||
/// <param name="path">资源路径</param>
|
||
/// <returns>如果成功增加返回 true,如果资源不存在返回 false</returns>
|
||
public bool AddReference(string path)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(path))
|
||
throw new ArgumentException(PathCannotBeNullOrEmptyMessage, nameof(path));
|
||
|
||
if (_cache.TryGetValue(path, out var entry))
|
||
{
|
||
lock (_lock)
|
||
{
|
||
entry.ReferenceCount++;
|
||
entry.LastAccessTime = DateTime.UtcNow;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 减少资源的引用计数
|
||
/// </summary>
|
||
/// <param name="path">资源路径</param>
|
||
/// <returns>减少后的引用计数,如果资源不存在返回 -1</returns>
|
||
public int RemoveReference(string path)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(path))
|
||
throw new ArgumentException(PathCannotBeNullOrEmptyMessage, nameof(path));
|
||
|
||
if (_cache.TryGetValue(path, out var entry))
|
||
{
|
||
lock (_lock)
|
||
{
|
||
entry.ReferenceCount--;
|
||
return entry.ReferenceCount;
|
||
}
|
||
}
|
||
|
||
return -1;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取资源的引用计数
|
||
/// </summary>
|
||
/// <param name="path">资源路径</param>
|
||
/// <returns>引用计数,如果资源不存在返回 -1</returns>
|
||
public int GetReferenceCount(string path)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(path))
|
||
throw new ArgumentException(PathCannotBeNullOrEmptyMessage, nameof(path));
|
||
|
||
if (_cache.TryGetValue(path, out var entry))
|
||
{
|
||
lock (_lock)
|
||
{
|
||
return entry.ReferenceCount;
|
||
}
|
||
}
|
||
|
||
return -1;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取所有已缓存资源的路径
|
||
/// </summary>
|
||
/// <returns>资源路径集合</returns>
|
||
public IEnumerable<string> GetAllPaths()
|
||
{
|
||
return _cache.Keys.ToList();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取所有引用计数为 0 的资源路径
|
||
/// </summary>
|
||
/// <returns>资源路径集合</returns>
|
||
public IEnumerable<string> GetUnreferencedPaths()
|
||
{
|
||
var unreferencedPaths = new List<string>();
|
||
|
||
foreach (var kvp in _cache)
|
||
{
|
||
lock (_lock)
|
||
{
|
||
if (kvp.Value.ReferenceCount <= 0)
|
||
{
|
||
unreferencedPaths.Add(kvp.Key);
|
||
}
|
||
}
|
||
}
|
||
|
||
return unreferencedPaths;
|
||
}
|
||
}
|