#region Apache License 2.0 /* Nuclex .NET Framework Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #endregion // Apache License 2.0 using System; using System.Collections.Generic; namespace Nuclex.Support.Collections { /// Pool that recycles objects in order to avoid garbage build-up /// Type of objects being pooled /// /// /// Use this class to recycle objects instead of letting them become garbage, /// creating new instances each time. The Pool class is designed to either be /// used on its own or as a building block for a static class that wraps it. /// /// /// Special care has to be taken to revert the entire state of a recycled /// object when it is returned to the pool. For example, events will need to /// have their subscriber lists emptied to avoid sending out events to the /// wrong subscribers and accumulating more and more subscribers each time /// they are reused. /// /// /// To simplify such cleanup, pooled objects can implement the IRecyclable /// interface. When an object is returned to the pool, the pool will /// automatically call its IRecyclable.Recycle() method. /// /// public class Pool { /// Default number of recyclable objects the pool will store public const int DefaultPoolSize = 64; /// Initializes a new pool using the default capacity public Pool() : this(DefaultPoolSize, null, null) { } /// Initializes a new pool using the default capacity /// Delegate that will be used to create new items public Pool(Func createNewDelegate) : this(DefaultPoolSize, createNewDelegate, null) { } /// Initializes a new pool using the default capacity /// Delegate that will be used to create new items /// Delegate that will be used to recycle items public Pool(Func createNewDelegate, Action recycleDelegate) : this(DefaultPoolSize, createNewDelegate, recycleDelegate) { } /// Initializes a new pool using a user-specified capacity /// Capacity of the pool public Pool(int capacity) : this(capacity, null, null) { } /// Initializes a new pool using a user-specified capacity /// Capacity of the pool /// Delegate that will be used to create new items public Pool(int capacity, Func createNewDelegate) : this(capacity, createNewDelegate, null) { } /// Initializes a new pool using a user-specified capacity /// Capacity of the pool /// Delegate that will be used to create new items /// Delegate that will be used to recycle items public Pool(int capacity, Func createNewDelegate, Action recycleDelegate) { Capacity = capacity; if(createNewDelegate == null) { if(!typeof(TItem).HasDefaultConstructor()) { throw new ArgumentException( "Type " + typeof(TItem).Name + " has no default constructor and " + "requires a custom 'create instance' delegate" ); } createNewDelegate = new Func(Activator.CreateInstance); } if(recycleDelegate == null) { recycleDelegate = new Action(callRecycleIfSupported); } this.createNewDelegate = createNewDelegate; this.recycleDelegate = recycleDelegate; } /// /// Returns a new or recycled instance of the types managed by the pool /// /// A new or recycled instance public TItem Get() { lock(this) { if(this.items.Count > 0) { return this.items.Dequeue(); } else { return this.createNewDelegate(); } } } /// /// Redeems an instance that is no longer used to be recycled by the pool /// /// The instance that will be redeemed public void Redeem(TItem item) { // Call Recycle() when the object is redeemed (instead of when it leaves // the pool again) in order to eliminate any references the object may hold // to other objects. this.recycleDelegate(item); lock(this) { if(this.items.Count < this.capacity) { this.items.Enqueue(item); } } } /// Number of objects the pool can retain /// /// Changing this value causes the pool to be emtpied. It is recommended that /// you only read the pool's capacity, never change it. /// public int Capacity { get { return this.capacity; } set { this.capacity = value; this.items = new Queue(value); } } /// /// Calls the Recycle() method on an objects if it implements /// the IRecyclable interface /// /// /// Object whose Recycle() method will be called if supported by the object /// private static void callRecycleIfSupported(TItem item) { IRecyclable recycleable = item as IRecyclable; if(recycleable != null) { recycleable.Recycle(); } } /// Objects being retained for recycling private Queue items; /// Capacity of the pool /// /// Required because the Queue class doesn't allow this value to be retrieved /// private int capacity; /// Delegate used to create new instances of the pool's type private Func createNewDelegate; /// Delegate used to recycle instances private Action recycleDelegate; } } // namespace Nuclex.Support.Collections