using AxibugEmuOnline.Client.ClientCore; using System; using System.Collections.Generic; using System.Reflection; using System.Text; namespace AxibugEmuOnline.Client.Common { 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(); App.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(); App.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(); App.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(); App.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) { App.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()); App.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()); App.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--; } /************************************************************************************************************************/ } } }