diff --git a/GFramework.Core.Abstractions/internals/IsExternalInit.cs b/GFramework.Core.Abstractions/internals/IsExternalInit.cs
new file mode 100644
index 0000000..880389a
--- /dev/null
+++ b/GFramework.Core.Abstractions/internals/IsExternalInit.cs
@@ -0,0 +1,18 @@
+// IsExternalInit.cs
+// This type is required to support init-only setters and record types
+// when targeting netstandard2.0 or older frameworks.
+
+#if !NET5_0_OR_GREATER
+using System.ComponentModel;
+
+namespace System.Runtime.CompilerServices;
+
+///
+/// 提供一个占位符类型,用于支持 C# 9.0 的 init 访问器功能。
+/// 该类型在 .NET 5.0 及更高版本中已内置,因此仅在较低版本的 .NET 中定义。
+///
+[EditorBrowsable(EditorBrowsableState.Never)]
+internal static class IsExternalInit
+{
+}
+#endif
\ No newline at end of file
diff --git a/GFramework.Core.Abstractions/pool/IObjectPoolSystem.cs b/GFramework.Core.Abstractions/pool/IObjectPoolSystem.cs
index c7c8b30..4f73a78 100644
--- a/GFramework.Core.Abstractions/pool/IObjectPoolSystem.cs
+++ b/GFramework.Core.Abstractions/pool/IObjectPoolSystem.cs
@@ -27,4 +27,39 @@ public interface IObjectPoolSystem
/// 清空所有对象池
///
void Clear();
+
+ ///
+ /// 获取指定池的当前大小
+ ///
+ /// 对象池的键
+ /// 池中可用对象的数量
+ int GetPoolSize(TKey key);
+
+ ///
+ /// 获取指定池的活跃对象数量
+ ///
+ /// 对象池的键
+ /// 已被获取但未释放的对象数量
+ int GetActiveCount(TKey key);
+
+ ///
+ /// 设置指定池的最大容量
+ ///
+ /// 对象池的键
+ /// 最大容量,超过此容量的对象将被销毁而不是放回池中
+ void SetMaxCapacity(TKey key, int maxCapacity);
+
+ ///
+ /// 预热对象池,提前创建指定数量的对象
+ ///
+ /// 对象池的键
+ /// 要预创建的对象数量
+ void Prewarm(TKey key, int count);
+
+ ///
+ /// 获取指定池的统计信息
+ ///
+ /// 对象池的键
+ /// 池的统计信息
+ PoolStatistics GetStatistics(TKey key);
}
\ No newline at end of file
diff --git a/GFramework.Core.Abstractions/pool/PoolStatistics.cs b/GFramework.Core.Abstractions/pool/PoolStatistics.cs
new file mode 100644
index 0000000..45bfdc5
--- /dev/null
+++ b/GFramework.Core.Abstractions/pool/PoolStatistics.cs
@@ -0,0 +1,42 @@
+namespace GFramework.Core.Abstractions.pool;
+
+///
+/// 对象池统计信息
+///
+public class PoolStatistics
+{
+ ///
+ /// 池中当前可用对象数量
+ ///
+ public int AvailableCount { get; init; }
+
+ ///
+ /// 当前活跃(已获取但未释放)的对象数量
+ ///
+ public int ActiveCount { get; init; }
+
+ ///
+ /// 池的最大容量限制,0 表示无限制
+ ///
+ public int MaxCapacity { get; init; }
+
+ ///
+ /// 累计创建的对象总数
+ ///
+ public int TotalCreated { get; init; }
+
+ ///
+ /// 累计获取对象的次数
+ ///
+ public int TotalAcquired { get; init; }
+
+ ///
+ /// 累计释放对象的次数
+ ///
+ public int TotalReleased { get; init; }
+
+ ///
+ /// 累计销毁的对象数量(超过容量限制被销毁的对象)
+ ///
+ public int TotalDestroyed { get; init; }
+}
diff --git a/GFramework.Core.Tests/extensions/ArrayPoolExtensionsTests.cs b/GFramework.Core.Tests/extensions/ArrayPoolExtensionsTests.cs
new file mode 100644
index 0000000..fee502f
--- /dev/null
+++ b/GFramework.Core.Tests/extensions/ArrayPoolExtensionsTests.cs
@@ -0,0 +1,194 @@
+using System.Buffers;
+using GFramework.Core.extensions;
+using NUnit.Framework;
+
+namespace GFramework.Core.Tests.extensions;
+
+///
+/// 测试 ArrayPoolExtensions 的功能
+///
+[TestFixture]
+public class ArrayPoolExtensionsTests
+{
+ private ArrayPool _pool = null!;
+
+ [SetUp]
+ public void SetUp()
+ {
+ _pool = ArrayPool.Shared;
+ }
+
+ [Test]
+ public void RentArray_Should_Return_Array()
+ {
+ // Act
+ var array = _pool.RentArray(100);
+
+ // Assert
+ Assert.That(array, Is.Not.Null);
+ Assert.That(array.Length, Is.GreaterThanOrEqualTo(100));
+
+ // Cleanup
+ _pool.ReturnArray(array);
+ }
+
+ [Test]
+ public void ReturnArray_Should_Not_Throw()
+ {
+ // Arrange
+ var array = _pool.RentArray(100);
+
+ // Act & Assert
+ Assert.DoesNotThrow(() => _pool.ReturnArray(array));
+ }
+
+ [Test]
+ public void ReturnArray_With_Clear_Should_Clear_Array()
+ {
+ // Arrange
+ var array = _pool.RentArray(10);
+ array[0] = 42;
+ array[5] = 99;
+
+ // Act
+ _pool.ReturnArray(array, clearArray: true);
+
+ // Assert
+ Assert.That(array[0], Is.EqualTo(0));
+ Assert.That(array[5], Is.EqualTo(0));
+ }
+
+ [Test]
+ public void GetScoped_Should_Return_Disposable_Wrapper()
+ {
+ // Act
+ using var scoped = _pool.GetScoped(100);
+
+ // Assert
+ Assert.That(scoped.Array, Is.Not.Null);
+ Assert.That(scoped.Array.Length, Is.GreaterThanOrEqualTo(100));
+ Assert.That(scoped.Length, Is.GreaterThanOrEqualTo(100));
+ }
+
+ [Test]
+ public void GetScoped_Should_Auto_Return_On_Dispose()
+ {
+ // Arrange & Act
+ int[] array;
+ using (var scoped = _pool.GetScoped(100))
+ {
+ array = scoped.Array;
+ array[0] = 42;
+ }
+
+ // Assert - 如果没有异常就说明正常归还了
+ Assert.Pass();
+ }
+
+ [Test]
+ public void ScopedArray_AsSpan_Should_Return_Span()
+ {
+ // Arrange
+ using var scoped = _pool.GetScoped(100);
+
+ // Act
+ var span = scoped.AsSpan();
+
+ // Assert
+ Assert.That(span.Length, Is.EqualTo(scoped.Length));
+ }
+
+ [Test]
+ public void ScopedArray_AsSpan_With_Range_Should_Return_Slice()
+ {
+ // Arrange
+ using var scoped = _pool.GetScoped(100);
+
+ // Act
+ var span = scoped.AsSpan(10, 20);
+
+ // Assert
+ Assert.That(span.Length, Is.EqualTo(20));
+ }
+
+ [Test]
+ public void GetScoped_With_ClearOnReturn_Should_Clear_Array()
+ {
+ // Arrange
+ int[] array;
+ using (var scoped = _pool.GetScoped(10, clearOnReturn: true))
+ {
+ array = scoped.Array;
+ array[0] = 42;
+ array[5] = 99;
+ }
+
+ // Assert
+ Assert.That(array[0], Is.EqualTo(0));
+ Assert.That(array[5], Is.EqualTo(0));
+ }
+
+ [Test]
+ public void Multiple_Scoped_Arrays_Should_Work_Independently()
+ {
+ // Act
+ using var scoped1 = _pool.GetScoped(50);
+ using var scoped2 = _pool.GetScoped(100);
+
+ scoped1.Array[0] = 1;
+ scoped2.Array[0] = 2;
+
+ // Assert
+ Assert.That(scoped1.Array[0], Is.EqualTo(1));
+ Assert.That(scoped2.Array[0], Is.EqualTo(2));
+ Assert.That(scoped1.Array, Is.Not.SameAs(scoped2.Array));
+ }
+
+ [Test]
+ public void RentArray_Should_Be_Reusable()
+ {
+ // Arrange
+ var array1 = _pool.RentArray(100);
+ array1[0] = 42;
+ _pool.ReturnArray(array1, clearArray: true);
+
+ // Act
+ var array2 = _pool.RentArray(100);
+
+ // Assert
+ // 可能是同一个数组(已清空),也可能是不同的数组
+ Assert.That(array2, Is.Not.Null);
+ Assert.That(array2.Length, Is.GreaterThanOrEqualTo(100));
+
+ // Cleanup
+ _pool.ReturnArray(array2);
+ }
+
+ [Test]
+ public void ScopedArray_Should_Work_With_Using_Declaration()
+ {
+ // Act
+ using var scoped = _pool.GetScoped(50);
+ scoped.Array[0] = 123;
+
+ // Assert
+ Assert.That(scoped.Array[0], Is.EqualTo(123));
+ }
+
+ [Test]
+ public void AsSpan_Should_Allow_Span_Operations()
+ {
+ // Arrange
+ using var scoped = _pool.GetScoped(10);
+ var span = scoped.AsSpan();
+
+ // Act
+ span.Fill(42);
+
+ // Assert
+ for (var i = 0; i < span.Length; i++)
+ {
+ Assert.That(span[i], Is.EqualTo(42));
+ }
+ }
+}
diff --git a/GFramework.Core.Tests/pool/ObjectPoolTests.cs b/GFramework.Core.Tests/pool/ObjectPoolTests.cs
index b3fa16c..0ce50d9 100644
--- a/GFramework.Core.Tests/pool/ObjectPoolTests.cs
+++ b/GFramework.Core.Tests/pool/ObjectPoolTests.cs
@@ -119,6 +119,137 @@ public class ObjectPoolTests
var obj2 = _pool.Acquire("test");
Assert.That(obj2.OnAcquireCalled, Is.True);
}
+
+ ///
+ /// 验证GetPoolSize应该返回正确的池大小
+ ///
+ [Test]
+ public void GetPoolSize_Should_Return_Correct_Size()
+ {
+ // Arrange
+ var obj1 = _pool.Acquire("test");
+ var obj2 = _pool.Acquire("test");
+
+ // Act & Assert
+ Assert.That(_pool.GetPoolSize("test"), Is.EqualTo(0));
+
+ _pool.Release("test", obj1);
+ Assert.That(_pool.GetPoolSize("test"), Is.EqualTo(1));
+
+ _pool.Release("test", obj2);
+ Assert.That(_pool.GetPoolSize("test"), Is.EqualTo(2));
+ }
+
+ ///
+ /// 验证GetActiveCount应该返回正确的活跃对象数量
+ ///
+ [Test]
+ public void GetActiveCount_Should_Return_Correct_Count()
+ {
+ // Arrange & Act & Assert
+ Assert.That(_pool.GetActiveCount("test"), Is.EqualTo(0));
+
+ var obj1 = _pool.Acquire("test");
+ Assert.That(_pool.GetActiveCount("test"), Is.EqualTo(1));
+
+ var obj2 = _pool.Acquire("test");
+ Assert.That(_pool.GetActiveCount("test"), Is.EqualTo(2));
+
+ _pool.Release("test", obj1);
+ Assert.That(_pool.GetActiveCount("test"), Is.EqualTo(1));
+
+ _pool.Release("test", obj2);
+ Assert.That(_pool.GetActiveCount("test"), Is.EqualTo(0));
+ }
+
+ ///
+ /// 验证SetMaxCapacity应该限制池的最大容量
+ ///
+ [Test]
+ public void SetMaxCapacity_Should_Limit_Pool_Size()
+ {
+ // Arrange
+ _pool.SetMaxCapacity("test", 2);
+ var obj1 = _pool.Acquire("test");
+ var obj2 = _pool.Acquire("test");
+ var obj3 = _pool.Acquire("test");
+
+ // Act
+ _pool.Release("test", obj1);
+ _pool.Release("test", obj2);
+ _pool.Release("test", obj3);
+
+ // Assert
+ Assert.That(_pool.GetPoolSize("test"), Is.EqualTo(2));
+ Assert.That(obj3.OnPoolDestroyCalled, Is.True);
+ }
+
+ ///
+ /// 验证Prewarm应该预创建指定数量的对象
+ ///
+ [Test]
+ public void Prewarm_Should_Create_Objects_In_Advance()
+ {
+ // Act
+ _pool.Prewarm("test", 5);
+
+ // Assert
+ Assert.That(_pool.GetPoolSize("test"), Is.EqualTo(5));
+
+ var obj = _pool.Acquire("test");
+ Assert.That(_pool.GetPoolSize("test"), Is.EqualTo(4));
+ Assert.That(obj.OnReleaseCalled, Is.True); // 预热时调用了OnRelease
+ }
+
+ ///
+ /// 验证GetStatistics应该返回正确的统计信息
+ ///
+ [Test]
+ public void GetStatistics_Should_Return_Correct_Statistics()
+ {
+ // Arrange
+ _pool.SetMaxCapacity("test", 2);
+ _pool.Prewarm("test", 2);
+
+ var obj1 = _pool.Acquire("test");
+ var obj2 = _pool.Acquire("test");
+ var obj3 = _pool.Acquire("test"); // 这个会创建新对象
+
+ _pool.Release("test", obj1);
+ _pool.Release("test", obj2);
+ _pool.Release("test", obj3); // 这个会被销毁
+
+ // Act
+ var stats = _pool.GetStatistics("test");
+
+ // Assert
+ Assert.That(stats.AvailableCount, Is.EqualTo(2));
+ Assert.That(stats.ActiveCount, Is.EqualTo(0));
+ Assert.That(stats.MaxCapacity, Is.EqualTo(2));
+ Assert.That(stats.TotalCreated, Is.EqualTo(3)); // 2个预热 + 1个新创建
+ Assert.That(stats.TotalAcquired, Is.EqualTo(3));
+ Assert.That(stats.TotalReleased, Is.EqualTo(3));
+ Assert.That(stats.TotalDestroyed, Is.EqualTo(1));
+ }
+
+ ///
+ /// 验证不存在的池应该返回空统计信息
+ ///
+ [Test]
+ public void GetStatistics_Should_Return_Empty_For_Nonexistent_Pool()
+ {
+ // Act
+ var stats = _pool.GetStatistics("nonexistent");
+
+ // Assert
+ Assert.That(stats.AvailableCount, Is.EqualTo(0));
+ Assert.That(stats.ActiveCount, Is.EqualTo(0));
+ Assert.That(stats.MaxCapacity, Is.EqualTo(0));
+ Assert.That(stats.TotalCreated, Is.EqualTo(0));
+ Assert.That(stats.TotalAcquired, Is.EqualTo(0));
+ Assert.That(stats.TotalReleased, Is.EqualTo(0));
+ Assert.That(stats.TotalDestroyed, Is.EqualTo(0));
+ }
}
///
diff --git a/GFramework.Core.Tests/pool/StringBuilderPoolTests.cs b/GFramework.Core.Tests/pool/StringBuilderPoolTests.cs
new file mode 100644
index 0000000..66cde30
--- /dev/null
+++ b/GFramework.Core.Tests/pool/StringBuilderPoolTests.cs
@@ -0,0 +1,116 @@
+using GFramework.Core.pool;
+using NUnit.Framework;
+
+namespace GFramework.Core.Tests.pool;
+
+///
+/// 测试 StringBuilderPool 的功能
+///
+[TestFixture]
+public class StringBuilderPoolTests
+{
+ [Test]
+ public void Rent_Should_Return_StringBuilder()
+ {
+ // Act
+ var sb = StringBuilderPool.Rent();
+
+ // Assert
+ Assert.That(sb, Is.Not.Null);
+ Assert.That(sb.Length, Is.EqualTo(0));
+ }
+
+ [Test]
+ public void Rent_Should_Respect_Capacity()
+ {
+ // Act
+ var sb = StringBuilderPool.Rent(512);
+
+ // Assert
+ Assert.That(sb.Capacity, Is.GreaterThanOrEqualTo(512));
+ }
+
+ [Test]
+ public void Return_Should_Clear_StringBuilder()
+ {
+ // Arrange
+ var sb = StringBuilderPool.Rent();
+ sb.Append("Hello World");
+
+ // Act
+ StringBuilderPool.Return(sb);
+
+ // Assert
+ Assert.That(sb.Length, Is.EqualTo(0));
+ }
+
+ [Test]
+ public void Return_Should_Not_Throw_For_Large_Capacity()
+ {
+ // Arrange
+ var sb = StringBuilderPool.Rent(10000);
+
+ // Act & Assert
+ Assert.DoesNotThrow(() => StringBuilderPool.Return(sb));
+ }
+
+ [Test]
+ public void GetScoped_Should_Return_Disposable_Wrapper()
+ {
+ // Act
+ using var scoped = StringBuilderPool.GetScoped();
+
+ // Assert
+ Assert.That(scoped.Value, Is.Not.Null);
+ Assert.That(scoped.Value.Length, Is.EqualTo(0));
+ }
+
+ [Test]
+ public void GetScoped_Should_Auto_Return_On_Dispose()
+ {
+ // Arrange
+ var sb = StringBuilderPool.GetScoped().Value;
+ sb.Append("Test");
+
+ // Act
+ using (var scoped = StringBuilderPool.GetScoped())
+ {
+ scoped.Value.Append("Hello");
+ }
+
+ // Assert - 如果没有异常就说明正常归还了
+ Assert.Pass();
+ }
+
+ [Test]
+ public void StringBuilder_Should_Be_Reusable()
+ {
+ // Arrange
+ var sb = StringBuilderPool.Rent();
+ sb.Append("First use");
+ StringBuilderPool.Return(sb);
+
+ // Act
+ sb.Append("Second use");
+
+ // Assert
+ Assert.That(sb.ToString(), Is.EqualTo("Second use"));
+ }
+
+ [Test]
+ public void GetScoped_With_Using_Should_Work()
+ {
+ // Act
+ string result;
+ using (var scoped = StringBuilderPool.GetScoped())
+ {
+ scoped.Value.Append("Hello");
+ scoped.Value.Append(" ");
+ scoped.Value.Append("World");
+ result = scoped.Value.ToString();
+ }
+
+ // Assert
+ Assert.That(result, Is.EqualTo("Hello World"));
+ }
+}
diff --git a/GFramework.Core/extensions/ArrayPoolExtensions.cs b/GFramework.Core/extensions/ArrayPoolExtensions.cs
new file mode 100644
index 0000000..3a2d98f
--- /dev/null
+++ b/GFramework.Core/extensions/ArrayPoolExtensions.cs
@@ -0,0 +1,136 @@
+using System.Buffers;
+
+namespace GFramework.Core.extensions;
+
+///
+/// ArrayPool 扩展方法,提供更便捷的数组池操作
+///
+public static class ArrayPoolExtensions
+{
+ ///
+ /// 从数组池中租用数组
+ ///
+ /// 数组元素类型
+ /// 数组池实例
+ /// 最小长度
+ /// 租用的数组
+ ///
+ ///
+ /// var pool = ArrayPool<int>.Shared;
+ /// var array = pool.RentArray(100);
+ /// try
+ /// {
+ /// // 使用数组
+ /// }
+ /// finally
+ /// {
+ /// pool.ReturnArray(array);
+ /// }
+ ///
+ ///
+ public static T[] RentArray(this ArrayPool pool, int minimumLength)
+ {
+ ArgumentNullException.ThrowIfNull(pool);
+ return pool.Rent(minimumLength);
+ }
+
+ ///
+ /// 将数组归还到数组池
+ ///
+ /// 数组元素类型
+ /// 数组池实例
+ /// 要归还的数组
+ /// 是否清空数组内容
+ ///
+ ///
+ /// var pool = ArrayPool<int>.Shared;
+ /// var array = pool.RentArray(100);
+ /// try
+ /// {
+ /// // 使用数组
+ /// }
+ /// finally
+ /// {
+ /// pool.ReturnArray(array, clearArray: true);
+ /// }
+ ///
+ ///
+ public static void ReturnArray(this ArrayPool pool, T[] array, bool clearArray = false)
+ {
+ ArgumentNullException.ThrowIfNull(pool);
+ ArgumentNullException.ThrowIfNull(array);
+ pool.Return(array, clearArray);
+ }
+
+ ///
+ /// 获取一个作用域数组,使用完后自动归还
+ ///
+ /// 数组元素类型
+ /// 数组池实例
+ /// 最小长度
+ /// 归还时是否清空数组
+ /// 可自动释放的数组包装器
+ ///
+ ///
+ /// var pool = ArrayPool<int>.Shared;
+ /// using var scopedArray = pool.GetScoped(100);
+ /// var array = scopedArray.Array;
+ /// // 使用数组
+ /// // 自动归还
+ ///
+ ///
+ public static ScopedArray GetScoped(this ArrayPool pool, int minimumLength, bool clearOnReturn = false)
+ {
+ ArgumentNullException.ThrowIfNull(pool);
+ return new ScopedArray(pool, minimumLength, clearOnReturn);
+ }
+
+ ///
+ /// 可自动释放的数组包装器
+ ///
+ /// 数组元素类型
+ public readonly struct ScopedArray : IDisposable
+ {
+ private readonly ArrayPool _pool;
+ private readonly bool _clearOnReturn;
+
+ ///
+ /// 获取租用的数组
+ ///
+ public T[] Array { get; }
+
+ ///
+ /// 获取数组的长度
+ ///
+ public int Length => Array.Length;
+
+ internal ScopedArray(ArrayPool pool, int minimumLength, bool clearOnReturn)
+ {
+ _pool = pool;
+ _clearOnReturn = clearOnReturn;
+ Array = pool.Rent(minimumLength);
+ }
+
+ ///
+ /// 释放数组并归还到池中
+ ///
+ public void Dispose()
+ {
+ _pool.Return(Array, _clearOnReturn);
+ }
+
+ ///
+ /// 获取数组的 Span 视图
+ ///
+ /// 数组的 Span
+ public Span AsSpan() => Array.AsSpan();
+
+ ///
+ /// 获取数组指定范围的 Span 视图
+ ///
+ /// 起始索引
+ /// 长度
+ /// 数组指定范围的 Span
+ public Span AsSpan(int start, int length) => Array.AsSpan(start, length);
+ }
+}
diff --git a/GFramework.Core/pool/AbstractObjectPoolSystem.cs b/GFramework.Core/pool/AbstractObjectPoolSystem.cs
index a5b8110..95d0b93 100644
--- a/GFramework.Core/pool/AbstractObjectPoolSystem.cs
+++ b/GFramework.Core/pool/AbstractObjectPoolSystem.cs
@@ -12,9 +12,52 @@ public abstract class AbstractObjectPoolSystem
: AbstractSystem, IObjectPoolSystem where TObject : IPoolableObject where TKey : notnull
{
///
- /// 存储对象池的字典,键为池标识,值为对应类型的对象栈
+ /// 池信息类,用于管理对象池的核心数据结构和统计信息。
+ /// 包含对象栈、容量限制以及各类操作的统计计数。
///
- protected readonly Dictionary> Pools = new();
+ protected class PoolInfo
+ {
+ ///
+ /// 对象栈,用于存储可复用的对象实例。
+ ///
+ public Stack Stack { get; } = new();
+
+ ///
+ /// 池的最大容量限制,超过此数量时将不再创建新对象。
+ ///
+ public int MaxCapacity { get; set; }
+
+ ///
+ /// 总共创建的对象数量统计。
+ ///
+ public int TotalCreated { get; set; }
+
+ ///
+ /// 总共从池中获取的对象数量统计。
+ ///
+ public int TotalAcquired { get; set; }
+
+ ///
+ /// 总共归还到池中的对象数量统计。
+ ///
+ public int TotalReleased { get; set; }
+
+ ///
+ /// 总共销毁的对象数量统计。
+ ///
+ public int TotalDestroyed { get; set; }
+
+ ///
+ /// 当前活跃(正在使用)的对象数量统计。
+ ///
+ public int ActiveCount { get; set; }
+ }
+
+
+ ///
+ /// 存储对象池的字典,键为池标识,值为池信息
+ ///
+ protected readonly Dictionary Pools = new();
///
/// 获取对象池中的对象,如果池中没有可用对象则创建新的对象
@@ -23,16 +66,25 @@ public abstract class AbstractObjectPoolSystem
/// 获取到的对象实例
public TObject Acquire(TKey key)
{
- if (!Pools.TryGetValue(key, out var pool))
+ if (!Pools.TryGetValue(key, out var poolInfo))
{
- pool = new Stack();
- Pools[key] = pool;
+ poolInfo = new PoolInfo();
+ Pools[key] = poolInfo;
}
- var obj = pool.Count > 0
- ? pool.Pop()
- : Create(key);
+ TObject obj;
+ if (poolInfo.Stack.Count > 0)
+ {
+ obj = poolInfo.Stack.Pop();
+ }
+ else
+ {
+ obj = Create(key);
+ poolInfo.TotalCreated++;
+ }
+ poolInfo.TotalAcquired++;
+ poolInfo.ActiveCount++;
obj.OnAcquire();
return obj;
}
@@ -46,13 +98,26 @@ public abstract class AbstractObjectPoolSystem
{
obj.OnRelease();
- if (!Pools.TryGetValue(key, out var pool))
+ if (!Pools.TryGetValue(key, out var poolInfo))
{
- pool = new Stack();
- Pools[key] = pool;
+ poolInfo = new PoolInfo();
+ Pools[key] = poolInfo;
}
- pool.Push(obj);
+ poolInfo.TotalReleased++;
+ poolInfo.ActiveCount--;
+
+ // 检查容量限制
+ if (poolInfo.MaxCapacity > 0 && poolInfo.Stack.Count >= poolInfo.MaxCapacity)
+ {
+ // 超过容量限制,销毁对象
+ obj.OnPoolDestroy();
+ poolInfo.TotalDestroyed++;
+ }
+ else
+ {
+ poolInfo.Stack.Push(obj);
+ }
}
///
@@ -61,11 +126,108 @@ public abstract class AbstractObjectPoolSystem
public void Clear()
{
// 遍历所有对象池,调用每个对象的销毁方法
- foreach (var obj in Pools.Values.SelectMany(pool => pool)) obj.OnPoolDestroy();
+ foreach (var poolInfo in Pools.Values)
+ {
+ foreach (var obj in poolInfo.Stack)
+ {
+ obj.OnPoolDestroy();
+ }
+ }
Pools.Clear();
}
+ ///
+ /// 获取指定池的当前大小
+ ///
+ /// 对象池的键
+ /// 池中可用对象的数量
+ public int GetPoolSize(TKey key)
+ {
+ return Pools.TryGetValue(key, out var poolInfo) ? poolInfo.Stack.Count : 0;
+ }
+
+ ///
+ /// 获取指定池的活跃对象数量
+ ///
+ /// 对象池的键
+ /// 已被获取但未释放的对象数量
+ public int GetActiveCount(TKey key)
+ {
+ return Pools.TryGetValue(key, out var poolInfo) ? poolInfo.ActiveCount : 0;
+ }
+
+ ///
+ /// 设置指定池的最大容量
+ ///
+ /// 对象池的键
+ /// 最大容量,超过此容量的对象将被销毁而不是放回池中
+ public void SetMaxCapacity(TKey key, int maxCapacity)
+ {
+ if (!Pools.TryGetValue(key, out var poolInfo))
+ {
+ poolInfo = new PoolInfo();
+ Pools[key] = poolInfo;
+ }
+
+ poolInfo.MaxCapacity = maxCapacity;
+ }
+
+ ///
+ /// 预热对象池,提前创建指定数量的对象
+ ///
+ /// 对象池的键
+ /// 要预创建的对象数量
+ public void Prewarm(TKey key, int count)
+ {
+ if (!Pools.TryGetValue(key, out var poolInfo))
+ {
+ poolInfo = new PoolInfo();
+ Pools[key] = poolInfo;
+ }
+
+ for (var i = 0; i < count; i++)
+ {
+ var obj = Create(key);
+ poolInfo.TotalCreated++;
+ obj.OnRelease();
+ poolInfo.Stack.Push(obj);
+ }
+ }
+
+ ///
+ /// 获取指定池的统计信息
+ ///
+ /// 对象池的键
+ /// 池的统计信息
+ public PoolStatistics GetStatistics(TKey key)
+ {
+ if (!Pools.TryGetValue(key, out var poolInfo))
+ {
+ return new PoolStatistics
+ {
+ AvailableCount = 0,
+ ActiveCount = 0,
+ MaxCapacity = 0,
+ TotalCreated = 0,
+ TotalAcquired = 0,
+ TotalReleased = 0,
+ TotalDestroyed = 0
+ };
+ }
+
+ return new PoolStatistics
+ {
+ AvailableCount = poolInfo.Stack.Count,
+ ActiveCount = poolInfo.ActiveCount,
+ MaxCapacity = poolInfo.MaxCapacity,
+ TotalCreated = poolInfo.TotalCreated,
+ TotalAcquired = poolInfo.TotalAcquired,
+ TotalReleased = poolInfo.TotalReleased,
+ TotalDestroyed = poolInfo.TotalDestroyed
+ };
+ }
+
///
/// 创建一个新的对象实例(由子类决定怎么创建)
///
diff --git a/GFramework.Core/pool/StringBuilderPool.cs b/GFramework.Core/pool/StringBuilderPool.cs
new file mode 100644
index 0000000..ff49027
--- /dev/null
+++ b/GFramework.Core/pool/StringBuilderPool.cs
@@ -0,0 +1,111 @@
+using System.Text;
+
+namespace GFramework.Core.pool;
+
+///
+/// StringBuilder 对象池,提供高性能的字符串构建器复用
+///
+public static class StringBuilderPool
+{
+ private const int DefaultCapacity = 256;
+ private const int MaxRetainedCapacity = 4096;
+
+ ///
+ /// 从池中租用一个 StringBuilder
+ ///
+ /// 初始容量,默认为 256
+ /// StringBuilder 实例
+ ///
+ ///
+ /// var sb = StringBuilderPool.Rent();
+ /// try
+ /// {
+ /// sb.Append("Hello");
+ /// sb.Append(" World");
+ /// return sb.ToString();
+ /// }
+ /// finally
+ /// {
+ /// StringBuilderPool.Return(sb);
+ /// }
+ ///
+ ///
+ public static StringBuilder Rent(int capacity = DefaultCapacity)
+ {
+ var sb = new StringBuilder(capacity);
+ return sb;
+ }
+
+ ///
+ /// 将 StringBuilder 归还到池中
+ ///
+ /// 要归还的 StringBuilder
+ ///
+ ///
+ /// var sb = StringBuilderPool.Rent();
+ /// try
+ /// {
+ /// sb.Append("Hello World");
+ /// Console.WriteLine(sb.ToString());
+ /// }
+ /// finally
+ /// {
+ /// StringBuilderPool.Return(sb);
+ /// }
+ ///
+ ///
+ public static void Return(StringBuilder builder)
+ {
+ ArgumentNullException.ThrowIfNull(builder);
+
+ // 如果容量过大,不放回池中
+ if (builder.Capacity > MaxRetainedCapacity)
+ {
+ return;
+ }
+
+ builder.Clear();
+ }
+
+ ///
+ /// 获取一个 StringBuilder,使用完后自动归还
+ ///
+ /// 初始容量
+ /// 可自动释放的 StringBuilder 包装器
+ ///
+ ///
+ /// using var sb = StringBuilderPool.GetScoped();
+ /// sb.Value.Append("Hello");
+ /// sb.Value.Append(" World");
+ /// return sb.Value.ToString();
+ ///
+ ///
+ public static ScopedStringBuilder GetScoped(int capacity = DefaultCapacity)
+ {
+ return new ScopedStringBuilder(Rent(capacity));
+ }
+
+ ///
+ /// 可自动释放的 StringBuilder 包装器
+ ///
+ public readonly struct ScopedStringBuilder : IDisposable
+ {
+ ///
+ /// 获取 StringBuilder 实例
+ ///
+ public StringBuilder Value { get; }
+
+ internal ScopedStringBuilder(StringBuilder value)
+ {
+ Value = value;
+ }
+
+ ///
+ /// 释放 StringBuilder 并归还到池中
+ ///
+ public void Dispose()
+ {
+ Return(Value);
+ }
+ }
+}