//****************************************

        /// <summary>
        /// Adds a new pair to the dictionary
        /// </summary>
        /// <param name="key">The key of the item to add</param>
        /// <param name="value">The value that will be weakly referenced</param>
        /// <exception cref="ArgumentNullException">Value was null</exception>
        /// <exception cref="ArgumentException">Key already exists</exception>
        public void Add(TKey key, TValue value)
        {
            if (value == null)
            {
                throw new ArgumentNullException(nameof(value), "Cannot add null to a Weak Dictionary");
            }

            // Is this key already in the dictionary?
            if (_Dictionary.TryGetValue(key, out var MyHandle))
            {
                // Yes, is the object available?
                if (MyHandle.IsAlive)
                {
                    throw new ArgumentException("Key already exists in the Weak Dictionary");
                }

                // No, free the handle and replace it
                MyHandle.Dispose();

                _Dictionary[key] = new GCReference(value, _HandleType);
            }
            else
            {
                // Not in the dictionary, add it
                _Dictionary.Add(key, new GCReference(value, _HandleType));
            }
        }
        /// <summary>
        /// Adds an item to the dictionary
        /// </summary>
        /// <param name="key">The key of the item to add</param>
        /// <param name="value">The value of the item to add</param>
        /// <returns>True if the item was added, otherwise false</returns>
        /// <remarks>Will add if the key is not set, and replace if the value is no longer available</remarks>
        public bool TryAdd(TKey key, TValue value)
        {               //****************************************
            GCReference NewHandle;

            //****************************************

            if (value == null)
            {
                throw new InvalidOperationException("Cannot add null to a Weak Dictionary");
            }

            for (; ;)
            {
                // Is this key already in the dictionary?
                if (!_Dictionary.TryGetValue(key, out var MyHandle))
                {
                    // Key not found, so let's try and add it
                    // Create a GC Handle to reference the object
                    NewHandle = new GCReference(value, _HandleType);

                    // Try and add it to the dictionary
                    if (_Dictionary.TryAdd(key, NewHandle))
                    {
                        return(true);                        // Success, return the result
                    }
                    // Key was added concurrently, free the handle we no longer need
                    NewHandle.Dispose();

                    return(false);
                }

                // Key found, is the weak reference still valid?
                if (MyHandle.IsAlive)
                {
                    return(false);                    // Yes, can't add
                }
                // Target reference has vanished, we can replace it with the new value
                NewHandle = new GCReference(value, _HandleType);

                // Try and update the dictionary with the replacement value
                if (_Dictionary.TryUpdate(key, NewHandle, MyHandle))
                {
                    // Success, now we can safely expire the old handle
                    MyHandle.Dispose();

                    return(true);
                }

                // Key was updated elsewhere. Could have been removed or simply updated with a valid value elsewhere
                // Ditch the updated value and try again
                NewHandle.Dispose();
            }
        }
        //****************************************

        /// <summary>
        /// Adds or replaces a key/value pair
        /// </summary>
        /// <param name="key">The key to add or replace</param>
        /// <param name="value">The value to associate with the key</param>
        public void AddOrReplace(TKey key, TValue value)
        {               //****************************************
            GCReference NewHandle;

            //****************************************

            if (value == null)
            {
                throw new InvalidOperationException("Cannot add null to a Weak Dictionary");
            }

            for (; ;)
            {
                // Is this key already in the dictionary?
                if (_Dictionary.TryGetValue(key, out var MyHandle))
                {
                    try
                    {
                        var OldValue = (TValue?)MyHandle.Target;

                        // Yes. If the reference is the same, no need to change anything
                        if (object.ReferenceEquals(OldValue, value))
                        {
                            return;
                        }
                    }
                    catch (InvalidOperationException)
                    {
                        // The GCHandle was disposed, try again
                        continue;
                    }

                    // Reference has changed, create a new GCReference
                    NewHandle = new GCReference(value, _HandleType);

                    // Try and update the dictionary with the replacement value
                    if (_Dictionary.TryUpdate(key, NewHandle, MyHandle))
                    {
                        // Success, now we can safely expire the old handle
                        MyHandle.Dispose();

                        return;
                    }

                    // Key was updated elsewhere, ditch the updated value and try again
                    NewHandle.Dispose();

                    continue;
                }

                // Create a GC Handle to reference the object
                NewHandle = new GCReference(value, _HandleType);

                // Try and add it to the dictionary
                if (_Dictionary.TryAdd(key, NewHandle))
                {
                    return;                     // Success, return the result
                }
                // Key was added concurrently, free the handle we no longer need
                NewHandle.Dispose();

                // Loop back and try again
            }
        }
        /// <summary>
        /// Updates an item in the dictionary
        /// </summary>
        /// <param name="key">The key of the item to update</param>
        /// <param name="newValue">The new value of the item</param>
        /// <param name="oldValue">The old value we expect to replace</param>
        /// <returns>True if the key exists and old value was replaced with new value, otherwise false</returns>
        /// <remarks>Will return false if the reference has expired</remarks>
        public bool TryUpdate(TKey key, TValue newValue, TValue oldValue)
        {               //****************************************
            GCReference NewHandle;

            TValue?OldValue;

            //****************************************

            if (newValue == null)
            {
                throw new ArgumentNullException(nameof(newValue), "Cannot add null to a Weak Dictionary");
            }

            if (oldValue == null)
            {
                throw new ArgumentNullException(nameof(oldValue), "Cannot have a null in a Weak Dictionary");
            }

            for (; ;)
            {
                // Is this key already in the dictionary?
                if (!_Dictionary.TryGetValue(key, out var MyHandle))
                {
                    return(false);                    // No, update fails
                }
                try
                {
                    OldValue = (TValue?)MyHandle.Target;
                }
                catch (InvalidOperationException)
                {
                    // The GCHandle was disposed, try again
                    continue;
                }

                // Yes, is it what we expected?
                if (!object.ReferenceEquals(oldValue, OldValue))
                {
                    return(false);                    // No, update fails
                }
                // Yes. If the old and new references are the same, no need to change anything
                // Check now, rather than earlier, so we can return true if it was 'updated' correctly
                if (object.ReferenceEquals(OldValue, newValue))
                {
                    return(true);
                }

                // Reference has changed, create a new GCReference
                NewHandle = new GCReference(newValue, _HandleType);

                // Try and update the dictionary with the replacement value
                if (_Dictionary.TryUpdate(key, NewHandle, MyHandle))
                {
                    // Success, now we can safely expire the old handle
                    MyHandle.Dispose();

                    return(true);
                }

                // Key was updated elsewhere, ditch the updated value and try again
                NewHandle.Dispose();
            }
        }
        /// <summary>
        /// Updates an item if it exists in the dictionary
        /// </summary>
        /// <param name="key">The key of the item to update</param>
        /// <param name="updateCallback">A callback that performs the update</param>
        /// <param name="newValue">Receives the new value if the dictionary was updated</param>
        /// <returns>True if the item was updated, False if it does not exist or the reference expired</returns>
        public bool TryUpdate(TKey key, Func <TKey, TValue, TValue> updateCallback,
#if !NETSTANDARD2_0 && !NET40
                              [MaybeNullWhen(false)]
#endif
                              out TValue newValue)
        {               //****************************************
            GCReference NewHandle;

            TValue?OldValue;
            TValue NewValue;

            //****************************************

            for (; ;)
            {
                // Is this key already in the dictionary?
                if (!_Dictionary.TryGetValue(key, out var MyHandle))
                {
                    newValue = null !;

                    return(false);                    // No, update fails
                }

                try
                {
                    OldValue = (TValue?)MyHandle.Target;
                }
                catch (InvalidOperationException)
                {
                    // The GCHandle was disposed, try again
                    continue;
                }

                // Yes, is the reference still valid?
                if (OldValue == null)
                {
                    newValue = null !;

                    return(false);                    // No, update fails
                }

                // Yes, update it
                NewValue = updateCallback(key, OldValue);

                if (NewValue == null)
                {
                    throw new InvalidOperationException("Cannot add null to a Weak Dictionary");
                }

                // Yes. If the old and new references are the same, no need to change anything
                // Check now, rather than earlier, so we can return true if it was 'updated' correctly
                if (object.ReferenceEquals(OldValue, NewValue))
                {
                    newValue = NewValue;

                    return(true);
                }

                // Reference has changed, create a new GCReference
                NewHandle = new GCReference(NewValue, _HandleType);

                // Try and update the dictionary with the replacement value
                if (_Dictionary.TryUpdate(key, NewHandle, MyHandle))
                {
                    // Success, now we can safely expire the old handle
                    MyHandle.Dispose();

                    newValue = NewValue;

                    return(true);
                }

                // Key was updated elsewhere, ditch the updated value and try again
                NewHandle.Dispose();
            }
        }
        /// <summary>
        /// Adds or retrieves a value based on the key
        /// </summary>
        /// <param name="key">The key to add or retrieve</param>
        /// <param name="value">A value to associate if the key doesn't exist</param>
        /// <param name="wasAdded">Receives whether the value was added or simply retrieved</param>
        /// <returns>The new or existing value</returns>
        public TValue GetOrAdd(TKey key, TValue value, out bool wasAdded)
        {         //****************************************
            GCReference NewHandle;

            //****************************************

            if (value == null)
            {
                throw new InvalidOperationException("Cannot add null to a Weak Dictionary");
            }

            for (; ;)
            {
                // Is this key already in the dictionary?
                if (_Dictionary.TryGetValue(key, out var MyHandle))
                {
                    try
                    {
                        var MyValue = (TValue?)MyHandle.Target;

                        // Yes, does the target still exist?
                        if (MyValue != null)
                        {
                            wasAdded = false;

                            return(MyValue);                            // Yes, return it
                        }
                    }
                    catch (InvalidOperationException)
                    {
                        // The GCHandle was disposed, try again
                        continue;
                    }

                    // Target reference has vanished, replace it with the new value
                    NewHandle = new GCReference(value, _HandleType);

                    // Try and update the dictionary with the replacement value
                    if (_Dictionary.TryUpdate(key, NewHandle, MyHandle))
                    {
                        // Success, now we can safely expire the old handle
                        MyHandle.Dispose();

                        wasAdded = true;

                        return(value);
                    }

                    // Key was updated elsewhere, ditch the updated value and try again
                    NewHandle.Dispose();

                    continue;
                }

                // Key not found, so let's try and add it
                // Create a GC Handle to reference the object
                NewHandle = new GCReference(value, _HandleType);

                // Try and add it to the dictionary
                if (_Dictionary.TryAdd(key, NewHandle))
                {
                    wasAdded = true;

                    return(value);                    // Success, return the result
                }

                // Key was added concurrently, free the handle we no longer need
                NewHandle.Dispose();

                // Loop back and try again
            }
        }
        /// <summary>
        /// Adds or updates a key/value pair
        /// </summary>
        /// <param name="key">The key to add or replace</param>
        /// <param name="valueCallback">A callback providing the new value</param>
        /// <param name="updateCallback">A callback updating an existing value</param>
        /// <returns>The added or updated value</returns>
        public TValue AddOrUpdate(TKey key, Func <TKey, TValue> valueCallback, Func <TKey, TValue, TValue> updateCallback)
        {               //****************************************
            GCReference NewHandle;

            TValue?OldValue;
            TValue NewValue;

            //****************************************

            for (; ;)
            {
                // Is this key already in the dictionary?
                if (_Dictionary.TryGetValue(key, out var MyHandle))
                {
                    try
                    {
                        OldValue = (TValue?)MyHandle.Target;
                    }
                    catch (InvalidOperationException)
                    {
                        // The GCHandle was disposed, try again
                        continue;
                    }

                    // Yes, does the target still exist?
                    if (OldValue != null)
                    {
                        // Yes, let's try and update it
                        NewValue = updateCallback(key, OldValue);

                        // If the reference is the same, no need to change anything
                        if (object.ReferenceEquals(OldValue, NewValue))
                        {
                            return(NewValue);
                        }
                    }
                    else
                    {
                        // Target reference has vanished, replace it with the new value
                        NewValue = valueCallback(key);

                        if (NewValue == null)
                        {
                            throw new InvalidOperationException("Cannot add null to a Weak Dictionary");
                        }
                    }

                    // Reference has changed, create a new GCReference
                    NewHandle = new GCReference(NewValue, _HandleType);

                    // Try and update the dictionary with the replacement value
                    if (_Dictionary.TryUpdate(key, NewHandle, MyHandle))
                    {
                        // Success, now we can safely expire the old handle
                        MyHandle.Dispose();

                        return(NewValue);
                    }

                    // Key was updated elsewhere, ditch the updated value and try again
                    NewHandle.Dispose();

                    continue;
                }

                // Key not found, so let's try and add it
                NewValue = valueCallback(key);

                if (NewValue == null)
                {
                    throw new InvalidOperationException("Cannot add null to a Weak Dictionary");
                }

                // Create a GC Handle to reference the object
                NewHandle = new GCReference(NewValue, _HandleType);

                // Try and add it to the dictionary
                if (_Dictionary.TryAdd(key, NewHandle))
                {
                    return(NewValue);                    // Success, return the result
                }
                // Key was added concurrently, free the handle we no longer need
                NewHandle.Dispose();

                // Loop back and try again
            }
        }