So after learning the .NET core Queue type uses arrays internally and not nodes I thought I'd have a go at writing my own that does.
The strange thing about MyQueue is that when it has a size of 9200 nodes its Length property returns its correct size, but when it has 9300 nodes it throws a StackOverflowException.
I can't for the life of me work out why this is the case.
QueueNode
namespace MyQ.Core
{
    public class MyQueueNode<T> : IMyQueueNode<T>
    {
        private MyQueueNode<T> _nextNode;
        private T _value;
        //..
        public MyQueueNode(T value)
        {
            _value = value;
        }
        public void SetNextNode(ref MyQueueNode<T> nextNode)
        {
            _nextNode = nextNode;
        }
        public MyQueueNode<T> GetNextNode()
        {
            return _nextNode;
        }
        public T GetValue()
        {
            return _value;
        }
    }
}
MyQueue
namespace MyQ.Core
{
    public class MyQueue<T> : IMyQueue<T> where T : class
    {
        private volatile MyQueueNode<T> _headNode, _tailNode;
        //..
        public void Enqueue(T item)
        {
            MyQueueNode<T> newNode = new MyQueueNode<T>(item);
            if (_headNode == null)
            {
                _headNode = newNode;
                _tailNode = _headNode;
            }
            else
            {
                _tailNode.SetNextNode(ref newNode);
                _tailNode = newNode;
            } 
        }
        public T Dequeue()
        {
            if(_headNode == null)
            {
                throw new QueueEmptyException();
            }
            T value = _headNode.GetValue();
            _headNode = _headNode.GetNextNode();
            return value;
        }
        public T Peek()
        {
            if(_headNode == null)
            {
                return null;
            }
            return _headNode.GetValue();
        }
        public long Length => GetLength(_headNode);
        //..
        private long GetLength(MyQueueNode<T> node = null, long level = 0)
        {
            if(node == null)
            {
                return 0;
            }
            level++;
            if (node.GetNextNode() == null)
            {
                return level;
            }
            node = node.GetNextNode();
            return GetLength(node, level);
        }
    }
}
Test program
using System;
using MyQ.Core;
using System.Diagnostics;
namespace MyQ
{
    class Program
    {
        static void Main(string[] args)
        {
            IMyQueue<string> myQ = new MyQueue<string>();
            //..
            myQ.Enqueue("test 1");
            myQ.Enqueue("test 2");
            myQ.Enqueue("test 3");
            //..
            Console.WriteLine($"The length of the queue is: {myQ.Length}");
            //..
            Console.WriteLine("Peek: " + myQ.Peek()); // 1
            //..
            Console.WriteLine("Dequeue: " + myQ.Dequeue()); // 1
            Console.WriteLine("Dequeue: " + myQ.Dequeue()); // 2
            Console.WriteLine($"The length of the queue is: {myQ.Length}");
            Console.WriteLine("Dequeue: " + myQ.Dequeue()); // 3
            //..
            Console.WriteLine("About to test seed a queue. Press a key to start...");
            Console.ReadKey();
            TestSize(9200); // This works fine...
            TestSize(9300); // This one falls over...
            Console.ReadKey();
        }
        static void TestSize(long seedSize)
        {
            Stopwatch sw = new Stopwatch();
            sw.Start();
            IMyQueue<string> queue = new MyQueue<string>();
            for (int i = 0; i < seedSize; i++)
            {
                Console.WriteLine($"Queue {i}");
                queue.Enqueue(i.ToString());
            }
            sw.Stop();
            Console.WriteLine($"Done. Took {sw.Elapsed.TotalSeconds} seconds.");
            Console.WriteLine($"Queue length is: {queue.Length}");
        }
    }
}
 
     
    