Aggiungiamo un'altra risposta. Perché questo rispetto ad altri?
1) Semplicità. Cercare di garantire le dimensioni va bene, ma porta a una complessità non necessaria che può mostrare i suoi problemi.
2) Implementa IReadOnlyCollection, il che significa che puoi usare Linq su di esso e passarlo in una varietà di cose che si aspettano IEnumerable.
3) Nessun blocco. Molte delle soluzioni precedenti utilizzano i blocchi, il che non è corretto in una raccolta senza blocco.
4) Implementa lo stesso insieme di metodi, proprietà e interfacce di ConcurrentQueue, incluso IProducerConsumerCollection, che è importante se si desidera utilizzare la raccolta con BlockingCollection.
Questa implementazione potrebbe potenzialmente finire con più voci del previsto se TryDequeue fallisce, ma la frequenza con cui si verifica non sembra degna di un codice specializzato che inevitabilmente ostacolerà le prestazioni e causerà i suoi problemi imprevisti.
Se vuoi assolutamente garantire una dimensione, l'implementazione di un metodo Prune () o simile sembra l'idea migliore. È possibile utilizzare un blocco di lettura ReaderWriterLockSlim negli altri metodi (incluso TryDequeue) e prendere un blocco di scrittura solo durante l'eliminazione.
class ConcurrentFixedSizeQueue<T> : IProducerConsumerCollection<T>, IReadOnlyCollection<T>, ICollection {
readonly ConcurrentQueue<T> m_concurrentQueue;
readonly int m_maxSize;
public int Count => m_concurrentQueue.Count;
public bool IsEmpty => m_concurrentQueue.IsEmpty;
public ConcurrentFixedSizeQueue (int maxSize) : this(Array.Empty<T>(), maxSize) { }
public ConcurrentFixedSizeQueue (IEnumerable<T> initialCollection, int maxSize) {
if (initialCollection == null) {
throw new ArgumentNullException(nameof(initialCollection));
}
m_concurrentQueue = new ConcurrentQueue<T>(initialCollection);
m_maxSize = maxSize;
}
public void Enqueue (T item) {
m_concurrentQueue.Enqueue(item);
if (m_concurrentQueue.Count > m_maxSize) {
T result;
m_concurrentQueue.TryDequeue(out result);
}
}
public void TryPeek (out T result) => m_concurrentQueue.TryPeek(out result);
public bool TryDequeue (out T result) => m_concurrentQueue.TryDequeue(out result);
public void CopyTo (T[] array, int index) => m_concurrentQueue.CopyTo(array, index);
public T[] ToArray () => m_concurrentQueue.ToArray();
public IEnumerator<T> GetEnumerator () => m_concurrentQueue.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator () => GetEnumerator();
// Explicit ICollection implementations.
void ICollection.CopyTo (Array array, int index) => ((ICollection)m_concurrentQueue).CopyTo(array, index);
object ICollection.SyncRoot => ((ICollection) m_concurrentQueue).SyncRoot;
bool ICollection.IsSynchronized => ((ICollection) m_concurrentQueue).IsSynchronized;
// Explicit IProducerConsumerCollection<T> implementations.
bool IProducerConsumerCollection<T>.TryAdd (T item) => ((IProducerConsumerCollection<T>) m_concurrentQueue).TryAdd(item);
bool IProducerConsumerCollection<T>.TryTake (out T item) => ((IProducerConsumerCollection<T>) m_concurrentQueue).TryTake(out item);
public override int GetHashCode () => m_concurrentQueue.GetHashCode();
public override bool Equals (object obj) => m_concurrentQueue.Equals(obj);
public override string ToString () => m_concurrentQueue.ToString();
}