Vamos adicionar mais uma resposta. Por que isso em vez de outros?
1) Simplicidade. Tentar garantir que o tamanho é bom e bom, mas leva a uma complexidade desnecessária que pode apresentar seus próprios problemas.
2) Implementa IReadOnlyCollection, o que significa que você pode usar o Linq nele e passá-lo para uma variedade de coisas que esperam o IEnumerable.
3) Sem bloqueio. Muitas das soluções acima usam bloqueios, o que é incorreto em uma coleção sem bloqueio.
4) Implementa o mesmo conjunto de métodos, propriedades e interfaces que ConcurrentQueue faz, incluindo IProducerConsumerCollection, que é importante se você deseja usar a coleção com BlockingCollection.
Essa implementação pode potencialmente terminar com mais entradas do que o esperado se TryDequeue falhar, mas a frequência dessa ocorrência não parece valer um código especializado que inevitavelmente prejudicará o desempenho e causará seus próprios problemas inesperados.
Se você realmente deseja garantir um tamanho, implementar um Prune () ou método semelhante parece ser a melhor ideia. Você poderia usar um bloqueio de leitura ReaderWriterLockSlim nos outros métodos (incluindo TryDequeue) e obter um bloqueio de gravação apenas durante a remoção.
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();
}