AxibugEmuOnline_old/AxibugEmuOnline.Server/Common/ObjectPoolAuto.cs

402 lines
15 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Reflection;
using System.Text;
namespace AxibugEmuOnline.Server
{
public static class ObjectPoolAuto
{
/************************************************************************************************************************/
/// <summary>
/// 获取或者创建一个新的
/// </summary>
/// <remarks>Remember to <see cref="Release{T}(T)"/> 需要回收参见这个</remarks>
public static T Acquire<T>()
where T : class, new()
=> ObjectPool<T>.Acquire();
/// <summary>
/// 获取或者创建一个新的
/// </summary>
/// <remarks>Remember to <see cref="Release{T}(T)"/> 需要回收参见这个</remarks>
public static void Acquire<T>(out T item)
where T : class, new()
=> item = ObjectPool<T>.Acquire();
/************************************************************************************************************************/
/// <summary>
/// 回收对象
/// </summary>
public static void Release<T>(T item)
where T : class, new()
=> ObjectPool<T>.Release(item);
/// <summary>
/// 回收对象
/// </summary>
public static void Release<T>(ref T item) where T : class, new()
{
if (item != null)
{
ObjectPool<T>.Release(item);
item = null;
}
}
/************************************************************************************************************************/
public const string
NotClearError = " They must be cleared before being released to the pool and not modified after that.";
/************************************************************************************************************************/
/// <summary>
/// 获取或创建List
/// </summary>
/// <remarks>Remember to <see cref="Release{T}(List{T})"/> 回收参见此方法</remarks>
public static List<T> AcquireList<T>()
{
var list = ObjectPool<List<T>>.Acquire();
AppSrv.g_Log.Assert(list.Count == 0, "A pooled list is not empty." + NotClearError);
return list;
}
/// <summary>
/// 回收List
/// </summary>
public static void Release<T>(List<T> list)
{
list.Clear();
ObjectPool<List<T>>.Release(list);
}
/************************************************************************************************************************/
/// <summary>
/// 获取或创建Queue
/// </summary>
/// <remarks>Remember to <see cref="Release{T}(Queue{T})"/> 回收参见此方法</remarks>
public static Queue<T> AcquireQueue<T>()
{
var queue = ObjectPool<Queue<T>>.Acquire();
AppSrv.g_Log.Assert(queue.Count == 0, "A pooled list is not empty." + NotClearError);
return queue;
}
/// <summary>
/// 回收Queue
/// </summary>
public static void Release<T>(Queue<T> list)
{
list.Clear();
ObjectPool<Queue<T>>.Release(list);
}
/************************************************************************************************************************/
/// <summary>
/// 获取或创建HashSet
/// </summary>
public static HashSet<T> AcquireSet<T>()
{
var set = ObjectPool<HashSet<T>>.Acquire();
AppSrv.g_Log.Assert(set.Count == 0, "A pooled set is not empty." + NotClearError);
return set;
}
/// <summary>
/// 释放HashSet
/// </summary>
public static void Release<T>(HashSet<T> set)
{
set.Clear();
ObjectPool<HashSet<T>>.Release(set);
}
/************************************************************************************************************************/
/// <summary>
/// 获取一个字符串StringBuilder
/// </summary>
/// <remarks>Remember to <see cref="Release(StringBuilder)"/>回收参见这个</remarks>
public static StringBuilder AcquireStringBuilder()
{
var builder = ObjectPool<StringBuilder>.Acquire();
AppSrv.g_Log.Assert(builder.Length == 0, $"A pooled {nameof(StringBuilder)} is not empty." + NotClearError);
return builder;
}
/// <summary>
/// 回收 StringBuilder
/// </summary>
public static void Release(StringBuilder builder)
{
builder.Length = 0;
ObjectPool<StringBuilder>.Release(builder);
}
/// <summary>
/// 回收 StringBuilder
/// </summary>
public static string ReleaseToString(this StringBuilder builder)
{
var result = builder.ToString();
Release(builder);
return result;
}
/************************************************************************************************************************/
private static class Cache<T>
{
public static readonly Dictionary<MethodInfo, KeyValuePair<Func<T>, T>>
Results = new Dictionary<MethodInfo, KeyValuePair<Func<T>, T>>();
}
/// <summary>
/// 此方法主要用于频繁绘制缓存比如说GUI绘制
/// </summary>
public static T GetCachedResult<T>(Func<T> function)
{
var method = function.Method;
if (!Cache<T>.Results.TryGetValue(method, out var result))
{
result = new KeyValuePair<Func<T>, T>(function, function());
Cache<T>.Results.Add(method, result);
}
else if (result.Key != function)
{
AppSrv.g_Log.Warning(
$"{nameof(GetCachedResult)}<{typeof(T).Name}>" +
$" was previously called on {method.Name} with a different target." +
" This likely means that a new delegate is being passed into every call" +
" so it can't actually return the same cached object.");
}
return result.Value;
}
/************************************************************************************************************************/
public static class Disposable
{
/************************************************************************************************************************/
/// <summary>
/// Calls <see cref="ObjectPool{T}.Disposable.Acquire"/> to get a spare <see cref="List{T}"/> if
/// </summary>
public static IDisposable Acquire<T>(out T item)
where T : class, new()
=> ObjectPool<T>.Disposable.Acquire(out item);
/************************************************************************************************************************/
/// <summary>
/// Calls <see cref="ObjectPool{T}.Disposable.Acquire"/> to get a spare <see cref="List{T}"/> if
/// </summary>
public static IDisposable AcquireList<T>(out List<T> list)
{
var disposable = ObjectPool<List<T>>.Disposable.Acquire(out list, onRelease: (l) => l.Clear());
AppSrv.g_Log.Assert(list.Count == 0, "A pooled list is not empty." + NotClearError);
return disposable;
}
/************************************************************************************************************************/
/// <summary>
/// Calls <see cref="ObjectPool{T}.Disposable.Acquire"/> to get a spare <see cref="HashSet{T}"/> if
/// </summary>
public static IDisposable AcquireSet<T>(out HashSet<T> set)
{
var disposable = ObjectPool<HashSet<T>>.Disposable.Acquire(out set, onRelease: (s) => s.Clear());
AppSrv.g_Log.Assert(set.Count == 0, "A pooled set is not empty." + NotClearError);
return disposable;
}
/************************************************************************************************************************/
}
/************************************************************************************************************************/
}
public static class ObjectPool<T> where T : class, new()
{
/************************************************************************************************************************/
private static readonly List<T>
Items = new List<T>();
/************************************************************************************************************************/
/// <summary>The number of spare items currently in the pool.</summary>
public static int Count
{
get => Items.Count;
set
{
var count = Items.Count;
if (count < value)
{
if (Items.Capacity < value)
Items.Capacity = NextPowerOfTwo(value);
do
{
Items.Add(new T());
count++;
}
while (count < value);
}
else if (count > value)
{
Items.RemoveRange(value, count - value);
}
}
}
public static int NextPowerOfTwo(int value)
{
if (value <= 0)
{
throw new ArgumentException("Value must be greater than zero.");
}
int powerOfTwo = 1;
while (powerOfTwo < value)
{
powerOfTwo <<= 1; // equivalent to multiplying by 2
}
return powerOfTwo;
}
/************************************************************************************************************************/
/// <summary>
/// If the <see cref="Count"/> is less than the specified value, this method increases it to that value by
/// creating new objects.
/// </summary>
public static void SetMinCount(int count)
{
if (Count < count)
Count = count;
}
/************************************************************************************************************************/
/// <summary>The <see cref="List{T}.Capacity"/> of the internal list of spare items.</summary>
public static int Capacity
{
get => Items.Capacity;
set
{
if (Items.Count > value)
Items.RemoveRange(value, Items.Count - value);
Items.Capacity = value;
}
}
/************************************************************************************************************************/
/// <summary>Returns a spare item if there are any, or creates a new one.</summary>
/// <remarks>Remember to <see cref="Release(T)"/> it when you are done.</remarks>
public static T Acquire()
{
var count = Items.Count;
if (count == 0)
{
return new T();
}
else
{
count--;
var item = Items[count];
Items.RemoveAt(count);
return item;
}
}
/************************************************************************************************************************/
/// <summary>Adds the `item` to the list of spares so it can be reused.</summary>
public static void Release(T item)
{
Items.Add(item);
}
/************************************************************************************************************************/
/// <summary>Returns a description of the state of this pool.</summary>
public static string GetDetails()
{
return
$"{typeof(T).Name}" +
$" ({nameof(Count)} = {Items.Count}" +
$", {nameof(Capacity)} = {Items.Capacity}" +
")";
}
/************************************************************************************************************************/
/// <summary>
/// An <see cref="IDisposable"/> system to allow pooled objects to be acquired and released within <c>using</c>
/// statements instead of needing to manually release everything.
/// </summary>
public sealed class Disposable : IDisposable
{
/************************************************************************************************************************/
private static readonly List<Disposable> LazyStack = new List<Disposable>();
private static int _ActiveDisposables;
private T _Item;
private Action<T> _OnRelease;
/************************************************************************************************************************/
private Disposable() { }
/// <summary>
/// Calls <see cref="ObjectPool{T}.Acquire"/> to set the `item` and returns an <see cref="IDisposable"/>
/// that will call <see cref="Release(T)"/> on the `item` when disposed.
/// </summary>
public static IDisposable Acquire(out T item, Action<T> onRelease = null)
{
Disposable disposable;
if (LazyStack.Count <= _ActiveDisposables)
{
LazyStack.Add(disposable = new Disposable());
}
else
{
disposable = LazyStack[_ActiveDisposables];
}
_ActiveDisposables++;
disposable._Item = item = ObjectPool<T>.Acquire();
disposable._OnRelease = onRelease;
return disposable;
}
/************************************************************************************************************************/
void IDisposable.Dispose()
{
_OnRelease?.Invoke(_Item);
Release(_Item);
_ActiveDisposables--;
}
/************************************************************************************************************************/
}
}
}