Circular Buffer Implementation I'm implementing a "live analytics" feature and I need to always keep track of the last N items that were added to a collection so I figured I'd make a circular buffer since I couldn't find one. 

I tried to figure out to use Interlocked but couldn't find out how to use Increment or CompareExchange in an obviously correct way - so I took the lock.

Review/advice is appreciated.

public class CircularBuffer&lt;T&gt;
{
    private readonly T[] _data;
    private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
    private int _i = 0;
    private bool _isOneCycleFinished = false;
    private readonly int _size;

    public CircularBuffer(int size)
    {

        if (size &lt; 0)
        {
            throw new ArgumentException($"{nameof(size)} cannot be negative");
        }
        _i = 0;
        _data = new T[size];
        _size = size;
    }

    public IEnumerable&lt;T&gt; Latest()
    {
        try
        {
            _lock.EnterReadLock();
            if (!_isOneCycleFinished)
            {
                return _data.Take(_i).ToList();
            }
            return _data.Concat(_data).Skip(_i).Take(_size).ToList();
        }
        finally
        {
            _lock.ExitReadLock();
        }
    }


    public void Add(T t)
    {
        try
        {
            _lock.EnterWriteLock();
            _i = (_i + 1)%_size;
            if (!_isOneCycleFinished &amp;&amp; _i == 0)
            {
                _isOneCycleFinished = true;
            }
            _data[_i] = t;
        }
        finally
        {
            _lock.ExitWriteLock();
        }
    }
}
Like @Gareth mentioned in his comment it would be perfect to use the ConcurrentQueue&lt;T&gt;. 

You answered @Gareth the queue needs to be bounded in size but where is the problem. We can just add a check to the Add() method and if the queue's size is equal to the bounded size we will just dequeue one item.  

If the passed in size parameter of the constructor is 0 you will get a DivideByZeroException at _i = (_i + 1)%_size; and it doesn't really make sense to have a CircularBuffer with size == 0, does it ?

As you will notice in the adjusted code, I have placed the _lock.EnterWriteLock(); outside of the try..finally because what happens if the call to EnterWriteLock() fails ? 

public class CircularBuffer&lt;T&gt;
{
    private readonly ConcurrentQueue&lt;T&gt; _data;
    private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
    private readonly int _size;

    public CircularBuffer(int size)
    {
        if (size &lt; 1)
        {
            throw new ArgumentException($"{nameof(size)} cannot be negative or zero");
        }
        _data = new ConcurrentQueue&lt;T&gt;();
        _size = size;
    }

    public IEnumerable&lt;T&gt; Latest()
    {
        return _data.ToArray();
    }

    public void Add(T t)
    {
        _lock.EnterWriteLock();
        try
        {
            if (_data.Count == _size)
            {
                T value;
                _data.TryDequeue(out value);
            }

            _data.Enqueue(t);
        }
        finally
        {
            _lock.ExitWriteLock();
        }
    }
}


although using a ConcurrentQueue is IMO the cleaner way I will post a solution using a generic array as well like so  

public class CircularBuffer&lt;T&gt;
{
    private readonly ReaderWriterLockSlim slimLock = new ReaderWriterLockSlim();
    private readonly int capacity;
    private readonly T[] buffer;
    private readonly int upperBound;

    private int currentIndex = -1;
    private int currentStart = 0;

    public CircularBuffer(int size)
    {
        if (size &lt; 1)
        {
            throw new ArgumentException($"{nameof(size)} cannot be negative nor zero");
        }

        buffer = new T[size];
        capacity = size;
        upperBound = size - 1;
    }

    public void Add(T value)
    {
        slimLock.EnterWriteLock();
        try
        {

            if (IsFull || currentIndex == upperBound)
            {
                currentStart = FetchNextSlot(currentStart);
                IsFull = true;
            }

            currentIndex = FetchNextSlot(currentIndex);
            buffer[currentIndex] = value;

        }
        finally
        {
            slimLock.ExitWriteLock();
        }

    }

    public bool IsFull { get; private set; }

    private int FetchNextSlot(int value)
    {
        return (value + 1) % capacity;
    }

    public IEnumerable&lt;T&gt; Latest()
    {
        slimLock.EnterReadLock();
        try
        {
            return FetchItems().ToArray();
        }
        finally
        {
            slimLock.ExitReadLock();
        }
    }

    private IEnumerable&lt;T&gt; FetchItems()
    {
        IEnumerable&lt;T&gt; fetchedItems = Enumerable.Empty&lt;T&gt;();
        if (IsFull)
        {
            return FetchItems(currentStart, upperBound);
        }
        return fetchedItems.Concat(FetchItems(0, currentIndex));
    }
    private IEnumerable&lt;T&gt; FetchItems(int start, int end)
    {
        for (int i = start; i &lt;= end; i++)
        {
            yield return buffer[i];
        }
    }
}