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