Code/Concurrent/ConcurrentArray.cs
using System;
using System.Collections;
using System.Collections.Generic;

namespace ExtendedCollections.Concurrent;

public class ConcurrentArray<T> : IEnumerable<T>
{
    public int Length { get; }

    private readonly T[] _values;
    private readonly object[] _locks;

    public ConcurrentArray(int size)
    {
        Length = size;
        _values = new T[size];
        _locks = new object[size];
        for(int i = 0; i < size; ++i)
            _locks[i] = new();
    }

    public void Clear()
    {
        lock(_locks)
        {
            Array.Clear(_values);
        }
    }

    public void CopyTo(T[] array, int arrayIndex)
    {
        ArgumentNullException.ThrowIfNull(array);
        if(arrayIndex < 0)
            throw new ArgumentOutOfRangeException(nameof(arrayIndex), "Index is less than 0.");
        if(array.Length - arrayIndex < Length)
            throw new ArgumentException("Array has not enough space to copy to.", nameof(array));

        lock(_locks)
        {
            _values.CopyTo(array, arrayIndex);
        }
    }

    public int IndexOf(T item)
    {
        lock(_locks)
        {
            return Array.IndexOf(_values, item);
        }
    }

    public T GetOrAdd(int index, Func<T> provider)
    {
        lock(_locks)
        {
            lock(_locks[index])
            {
                var value = _values[index];
                if(value is null)
                {
                    value = provider();
                    _values[index] = value;
                }
                return value;
            }
        }
    }

    public T GetOrAdd(int index, T value)
    {
        lock(_locks)
        {
            lock(_locks[index])
            {
                var oldValue = _values[index];
                if(oldValue is null)
                {
                    _values[index] = value;
                    return value;
                }
                return oldValue;
            }
        }
    }

    public T this[int index]
    {
        get
        {
            lock(_locks)
            {
                lock(_locks[index])
                {
                    return _values[index];
                }
            }
        }
        set
        {
            lock(_locks)
            {
                lock(_locks[index])
                {
                    _values[index] = value;
                }
            }
        }
    }

    public IEnumerator<T> GetEnumerator()
    {
        for(int i = 0; i < Length; ++i)
        {
            lock(_locks)
            {
                lock(_locks[i])
                {
                    yield return _values[i];
                }
            }
        }
    }
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}