private void RemoveFromLinkedList(LeastRecentlyUsedCacheNode node)
        {
            var nextNode = node.Next;
            var prevNode = node.Previous;

            if (nextNode != null)
            {
                //move next node's previous to original node's previous
                nextNode.Previous = node.Previous;
            }
            if (prevNode != null)
            {
                //move previous node's next to original node's previous
                prevNode.Next = node.Next;
            }

            if (_head == node)
            {
                _head = nextNode;
            }
            if (_tail == node)
            {
                _tail = prevNode;
            }
        }
 private void MakeMostRecentlyUsed(LeastRecentlyUsedCacheNode node)
 {
     if (node == _head)
     {
         return;
     }
     RemoveFromLinkedList(node);
     AddToHead(node);
 }
 /// <summary>
 /// Removes all items from the cache.
 /// </summary>
 public void Clear()
 {
     using (_entriesSync.WriteLock())
     {
         _nodesByKey.Clear();
         _head         = null;
         _tail         = null;
         _currentCount = 0;
     }
 }
        private void AddToHead(LeastRecentlyUsedCacheNode node)
        {
            node.Previous = null;
            node.Next     = _head;

            if (_head != null)
            {
                _head.Previous = node;
            }
            _head = node;
        }
        /// <summary>
        /// Inserts a specified key and value into the cache.
        /// </summary>
        /// <param name="key">The key to add.</param>
        /// <param name="value">The value to add.</param>
        /// <remarks>
        /// Internall this method will :
        /// 1) If the cache is full, remove the LRU item from the cache.
        /// 2) If the specified key already exists in the cache, update the value held in the cache.
        /// 3) Mark the key/value added as the new LRU item in the cache.
        /// </remarks>
        /// <exception cref="ArgumentNullException"><paramref name="key"/> was null.</exception>
        public void Insert(TKey key, TValue value)
        {
            if (key == null)
            {
                throw new ArgumentNullException(nameof(key),
                                                $"Unable to insert into MRU Cache. {nameof(key)} parameter cannot be null");
            }

            using (_entriesSync.WriteLock())
            {
                LeastRecentlyUsedCacheNode node;
                if (!_nodesByKey.TryGetValue(key, out node))
                {
                    //not in cache so insert
                    //check another thread has not inserted the node
                    if (!_nodesByKey.TryGetValue(key, out node))
                    {
                        if (IsFull)
                        {
                            node = _tail;
                            _nodesByKey.Remove(_tail.Key);
                            node.Key   = key;
                            node.Value = value;
                        }
                        else
                        {
                            _currentCount++;
                            node = new LeastRecentlyUsedCacheNode {
                                Key = key, Value = value
                            };
                        }
                        _nodesByKey.Add(key, node);
                    }
                }
                else
                {
                    //exists in cache so locak the node and update its' value
                    lock (node)
                    {
                        node.Value = value;
                    }
                }

                MakeMostRecentlyUsed(node);

                if (_tail == null)
                {
                    _tail = _head;
                }
            }
        }