File: System\ServiceModel\SynchronizedKeyedCollection.cs
Web Access
Project: src\src\System.ServiceModel.Primitives\src\System.ServiceModel.Primitives.csproj (System.ServiceModel.Primitives)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
 
using System.Runtime;
using System.Runtime.InteropServices;
using System.ServiceModel;
 
namespace System.Collections.Generic
{
    [ComVisible(false)]
    public abstract class SynchronizedKeyedCollection<K, T> : SynchronizedCollection<T>
    {
        private const int defaultThreshold = 0;
 
        private IEqualityComparer<K> _comparer;
        private Dictionary<K, T> _dictionary;
        private int _keyCount;
        private int _threshold;
 
        protected SynchronizedKeyedCollection()
        {
            _comparer = EqualityComparer<K>.Default;
            _threshold = int.MaxValue;
        }
 
        protected SynchronizedKeyedCollection(object syncRoot)
            : base(syncRoot)
        {
            _comparer = EqualityComparer<K>.Default;
            _threshold = int.MaxValue;
        }
 
        protected SynchronizedKeyedCollection(object syncRoot, IEqualityComparer<K> comparer)
            : base(syncRoot)
        {
            _comparer = comparer ?? throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException(nameof(comparer)));
            _threshold = int.MaxValue;
        }
 
        protected SynchronizedKeyedCollection(object syncRoot, IEqualityComparer<K> comparer, int dictionaryCreationThreshold)
            : base(syncRoot)
        {
            if (dictionaryCreationThreshold < -1)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(dictionaryCreationThreshold), dictionaryCreationThreshold,
                                                    SRP.Format(SRP.ValueMustBeInRange, -1, int.MaxValue)));
            }
            else if (dictionaryCreationThreshold == -1)
            {
                _threshold = int.MaxValue;
            }
            else
            {
                _threshold = dictionaryCreationThreshold;
            }
 
            _comparer = comparer ?? throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException(nameof(comparer)));
        }
 
        public T this[K key]
        {
            get
            {
                if (key == null)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException(nameof(key)));
                }
 
                lock (SyncRoot)
                {
                    if (_dictionary != null)
                    {
                        return _dictionary[key];
                    }
 
                    for (int i = 0; i < Items.Count; i++)
                    {
                        T item = Items[i];
                        if (_comparer.Equals(key, GetKeyForItem(item)))
                        {
                            return item;
                        }
                    }
 
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new KeyNotFoundException());
                }
            }
        }
 
        protected IDictionary<K, T> Dictionary
        {
            get { return _dictionary; }
        }
 
        private void AddKey(K key, T item)
        {
            if (_dictionary != null)
            {
                _dictionary.Add(key, item);
            }
            else if (_keyCount == _threshold)
            {
                CreateDictionary();
                _dictionary.Add(key, item);
            }
            else
            {
                if (Contains(key))
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SRP.CannotAddTwoItemsWithTheSameKeyToSynchronizedKeyedCollection0));
                }
 
                _keyCount++;
            }
        }
 
        protected void ChangeItemKey(T item, K newKey)
        {
            // check if the item exists in the collection
            if (!ContainsItem(item))
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SRP.ItemDoesNotExistInSynchronizedKeyedCollection0));
            }
 
            K oldKey = GetKeyForItem(item);
            if (!_comparer.Equals(newKey, oldKey))
            {
                if (newKey != null)
                {
                    AddKey(newKey, item);
                }
 
                if (oldKey != null)
                {
                    RemoveKey(oldKey);
                }
            }
        }
 
        protected override void ClearItems()
        {
            base.ClearItems();
 
            if (_dictionary != null)
            {
                _dictionary.Clear();
            }
 
            _keyCount = 0;
        }
 
        public bool Contains(K key)
        {
            if (key == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException(nameof(key)));
            }
 
            lock (SyncRoot)
            {
                if (_dictionary != null)
                {
                    return _dictionary.ContainsKey(key);
                }
 
                if (key != null)
                {
                    for (int i = 0; i < Items.Count; i++)
                    {
                        T item = Items[i];
                        if (_comparer.Equals(key, GetKeyForItem(item)))
                        {
                            return true;
                        }
                    }
                }
                return false;
            }
        }
 
        private bool ContainsItem(T item)
        {
            K key;
            if ((_dictionary == null) || ((key = GetKeyForItem(item)) == null))
            {
                return Items.Contains(item);
            }
 
            T itemInDict;
 
            if (_dictionary.TryGetValue(key, out itemInDict))
            {
                return EqualityComparer<T>.Default.Equals(item, itemInDict);
            }
 
            return false;
        }
 
        private void CreateDictionary()
        {
            _dictionary = new Dictionary<K, T>(_comparer);
 
            foreach (T item in Items)
            {
                K key = GetKeyForItem(item);
                if (key != null)
                {
                    _dictionary.Add(key, item);
                }
            }
        }
 
        protected abstract K GetKeyForItem(T item);
 
        protected override void InsertItem(int index, T item)
        {
            K key = GetKeyForItem(item);
 
            if (key != null)
            {
                AddKey(key, item);
            }
 
            base.InsertItem(index, item);
        }
 
        public bool Remove(K key)
        {
            if (key == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException(nameof(key)));
            }
 
            lock (SyncRoot)
            {
                if (_dictionary != null)
                {
                    if (_dictionary.ContainsKey(key))
                    {
                        return Remove(_dictionary[key]);
                    }
                    else
                    {
                        return false;
                    }
                }
                else
                {
                    for (int i = 0; i < Items.Count; i++)
                    {
                        if (_comparer.Equals(key, GetKeyForItem(Items[i])))
                        {
                            RemoveItem(i);
                            return true;
                        }
                    }
                    return false;
                }
            }
        }
 
        protected override void RemoveItem(int index)
        {
            K key = GetKeyForItem(Items[index]);
 
            if (key != null)
            {
                RemoveKey(key);
            }
 
            base.RemoveItem(index);
        }
 
        private void RemoveKey(K key)
        {
            if (!(key != null))
            {
                Fx.Assert("key shouldn't be null!");
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(key));
            }
            if (_dictionary != null)
            {
                _dictionary.Remove(key);
            }
            else
            {
                _keyCount--;
            }
        }
 
        protected override void SetItem(int index, T item)
        {
            K newKey = GetKeyForItem(item);
            K oldKey = GetKeyForItem(Items[index]);
 
            if (_comparer.Equals(newKey, oldKey))
            {
                if ((newKey != null) && (_dictionary != null))
                {
                    _dictionary[newKey] = item;
                }
            }
            else
            {
                if (newKey != null)
                {
                    AddKey(newKey, item);
                }
 
                if (oldKey != null)
                {
                    RemoveKey(oldKey);
                }
            }
            base.SetItem(index, item);
        }
    }
}