iothub/service/src/Common/ReadOnlyDictionary45.cs (505 lines of code) (raw):

// Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.Contracts; using System.Threading; namespace Microsoft.Azure.Devices.Common { /*============================================================ ** ** Class: ReadOnlyDictionary<TKey, TValue> ** ** <OWNER>gpaperin</OWNER> ** ** Purpose: Read-only wrapper for another generic dictionary. ** ===========================================================*/ /// <summary> /// Read-only wrapper for another generic dictionary. /// </summary> /// <typeparam name="TKey">Type to be used for keys.</typeparam> /// <typeparam name="TValue">Type to be used for values</typeparam> [Serializable] [DebuggerDisplay("Count = {Count}")] #pragma warning disable CA1710 // Identifiers should have correct suffix public sealed class ReadOnlyDictionary45<TKey, TValue> : IDictionary<TKey, TValue>, IDictionary #pragma warning restore CA1710 // Identifiers should have correct suffix { [NonSerialized] private object _syncRoot; [NonSerialized] private KeyCollection _keys; [NonSerialized] private ValueCollection _values; [NonSerialized] private IReadOnlyIndicator _readOnlyIndicator; /// <summary> /// Creates a readonly dictionary from a specified IDictionary /// </summary> /// <param name="dictionary">The source dictionary</param> public ReadOnlyDictionary45(IDictionary<TKey, TValue> dictionary) : this(dictionary, new AlwaysReadOnlyIndicator()) { } internal ReadOnlyDictionary45(IDictionary<TKey, TValue> dictionary, IReadOnlyIndicator readOnlyIndicator) { Dictionary = dictionary ?? throw new ArgumentNullException(nameof(dictionary)); Contract.EndContractBlock(); _readOnlyIndicator = readOnlyIndicator; } /// <summary> /// The source dictionary /// </summary> #pragma warning disable CS0628 // New protected member declared in sealed class protected IDictionary<TKey, TValue> Dictionary { get; private set; } #pragma warning restore CS0628 // New protected member declared in sealed class /// <summary> /// They keys in the dictionary /// </summary> public KeyCollection Keys { get { Contract.Ensures(Contract.Result<KeyCollection>() != null); if (_keys == null) { _keys = new KeyCollection(Dictionary.Keys, _readOnlyIndicator); } return _keys; } } /// <summary> /// The values in the dictionary /// </summary> public ValueCollection Values { get { Contract.Ensures(Contract.Result<ValueCollection>() != null); if (_values == null) { _values = new ValueCollection(Dictionary.Values, _readOnlyIndicator); } return _values; } } #region IDictionary<TKey, TValue> Members /// <summary> /// Reports whether a key exists in the dictionary /// </summary> /// <param name="key">The key to check</param> /// <returns>True if exists, othewise fals</returns> public bool ContainsKey(TKey key) { return Dictionary.ContainsKey(key); } ICollection<TKey> IDictionary<TKey, TValue>.Keys => Keys; /// <summary> /// Gets the value of the specified key, if exists /// </summary> /// <param name="key">The desired key</param> /// <param name="value">The value found</param> /// <returns>True if key was found</returns> public bool TryGetValue(TKey key, out TValue value) { return Dictionary.TryGetValue(key, out value); } ICollection<TValue> IDictionary<TKey, TValue>.Values => Values; /// <summary> /// Enables accessing values by indexing with a key /// </summary> /// <param name="key">The desired key</param> /// <returns>The corresponding value</returns> public TValue this[TKey key] { get { return Dictionary[key]; } } void IDictionary<TKey, TValue>.Add(TKey key, TValue value) { if (_readOnlyIndicator.IsReadOnly) { throw Fx.Exception.AsError(new NotSupportedException(Resources.ObjectIsReadOnly)); } Dictionary.Add(key, value); } bool IDictionary<TKey, TValue>.Remove(TKey key) { if (_readOnlyIndicator.IsReadOnly) { throw Fx.Exception.AsError(new NotSupportedException(Resources.ObjectIsReadOnly)); } return Dictionary.Remove(key); } TValue IDictionary<TKey, TValue>.this[TKey key] { get => Dictionary[key]; set { if (_readOnlyIndicator.IsReadOnly) { throw Fx.Exception.AsError(new NotSupportedException(Resources.ObjectIsReadOnly)); } Dictionary[key] = value; } } #endregion IDictionary<TKey, TValue> Members #region ICollection<KeyValuePair<TKey, TValue>> Members /// <summary> /// The count of items in the dictionary /// </summary> public int Count => Dictionary.Count; bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item) { return Dictionary.Contains(item); } void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) { Dictionary.CopyTo(array, arrayIndex); } bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly => true; void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item) { if (_readOnlyIndicator.IsReadOnly) { throw Fx.Exception.AsError(new NotSupportedException(Resources.ObjectIsReadOnly)); } Dictionary.Add(item); } void ICollection<KeyValuePair<TKey, TValue>>.Clear() { if (_readOnlyIndicator.IsReadOnly) { throw Fx.Exception.AsError(new NotSupportedException(Resources.ObjectIsReadOnly)); } Dictionary.Clear(); } bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item) { if (_readOnlyIndicator.IsReadOnly) { throw Fx.Exception.AsError(new NotSupportedException(Resources.ObjectIsReadOnly)); } return Dictionary.Remove(item); } #endregion ICollection<KeyValuePair<TKey, TValue>> Members #region IEnumerable<KeyValuePair<TKey, TValue>> Members /// <summary> /// Returns an enumerator /// </summary> /// <returns>The enumerator</returns> public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() { return Dictionary.GetEnumerator(); } #endregion IEnumerable<KeyValuePair<TKey, TValue>> Members #region IEnumerable Members IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)Dictionary).GetEnumerator(); } #endregion IEnumerable Members #region IDictionary Members private static bool IsCompatibleKey(object key) { if (key == null) { throw Fx.Exception.ArgumentNull(nameof(key)); } return key is TKey; } void IDictionary.Add(object key, object value) { if (_readOnlyIndicator.IsReadOnly) { throw Fx.Exception.AsError(new NotSupportedException(Resources.ObjectIsReadOnly)); } Dictionary.Add((TKey)key, (TValue)value); } void IDictionary.Clear() { if (_readOnlyIndicator.IsReadOnly) { throw Fx.Exception.AsError(new NotSupportedException(Resources.ObjectIsReadOnly)); } Dictionary.Clear(); } bool IDictionary.Contains(object key) { return IsCompatibleKey(key) && ContainsKey((TKey)key); } IDictionaryEnumerator IDictionary.GetEnumerator() { var d = Dictionary as IDictionary; return d != null ? d.GetEnumerator() : new DictionaryEnumerator(Dictionary); } bool IDictionary.IsFixedSize => true; bool IDictionary.IsReadOnly => true; ICollection IDictionary.Keys => Keys; void IDictionary.Remove(object key) { if (_readOnlyIndicator.IsReadOnly) { throw Fx.Exception.AsError(new NotSupportedException(Resources.ObjectIsReadOnly)); } Dictionary.Remove((TKey)key); } ICollection IDictionary.Values => Values; object IDictionary.this[object key] { get => IsCompatibleKey(key) ? this[(TKey)key] : (object)null; set { if (_readOnlyIndicator.IsReadOnly) { throw Fx.Exception.AsError(new NotSupportedException(Resources.ObjectIsReadOnly)); } Dictionary[(TKey)key] = (TValue)value; } } void ICollection.CopyTo(Array array, int index) { if (array == null) { Fx.Exception.ArgumentNull(nameof(array)); } if (array.Rank != 1 || array.GetLowerBound(0) != 0) { throw Fx.Exception.Argument(nameof(array), Resources.InvalidBufferSize); } if (index < 0 || index > array.Length) { throw Fx.Exception.ArgumentOutOfRange(nameof(index), index, Resources.ValueMustBeNonNegative); } if (array.Length - index < Count) { throw Fx.Exception.Argument(nameof(array), Resources.InvalidBufferSize); } var pairs = array as KeyValuePair<TKey, TValue>[]; if (pairs != null) { Dictionary.CopyTo(pairs, index); } else { var dictEntryArray = array as DictionaryEntry[]; if (dictEntryArray != null) { foreach (KeyValuePair<TKey, TValue> item in Dictionary) { dictEntryArray[index++] = new DictionaryEntry(item.Key, item.Value); } } else { if (!(array is object[] objects)) { throw Fx.Exception.Argument(nameof(array), Resources.InvalidBufferSize); } try { foreach (KeyValuePair<TKey, TValue> item in Dictionary) { objects[index++] = new KeyValuePair<TKey, TValue>(item.Key, item.Value); } } catch (ArrayTypeMismatchException) { throw Fx.Exception.Argument(nameof(array), Resources.InvalidBufferSize); } } } } bool ICollection.IsSynchronized => false; object ICollection.SyncRoot { get { if (_syncRoot == null) { var c = Dictionary as ICollection; if (c != null) { _syncRoot = c.SyncRoot; } else { System.Threading.Interlocked.CompareExchange<Object>(ref _syncRoot, new Object(), null); } } return _syncRoot; } } [Serializable] private struct DictionaryEnumerator : IDictionaryEnumerator { private readonly IDictionary<TKey, TValue> _dictionary; private IEnumerator<KeyValuePair<TKey, TValue>> _enumerator; public DictionaryEnumerator(IDictionary<TKey, TValue> dictionary) { _dictionary = dictionary; _enumerator = _dictionary.GetEnumerator(); } public DictionaryEntry Entry => new DictionaryEntry(_enumerator.Current.Key, _enumerator.Current.Value); public object Key => _enumerator.Current.Key; public object Value => _enumerator.Current.Value; public object Current => Entry; public bool MoveNext() { return _enumerator.MoveNext(); } public void Reset() { _enumerator.Reset(); } } #endregion IDictionary Members /// <summary> /// A collection of dictionary keys /// </summary> [DebuggerDisplay("Count = {Count}")] [Serializable] #pragma warning disable CA1034 // Nested types should not be visible public sealed class KeyCollection : ICollection<TKey>, ICollection #pragma warning restore CA1034 // Nested types should not be visible { private readonly ICollection<TKey> _collection; [NonSerialized] private object _syncRoot; [NonSerialized] private readonly IReadOnlyIndicator _readOnlyIndicator; internal KeyCollection(ICollection<TKey> collection, IReadOnlyIndicator readOnlyIndicator) { _collection = collection ?? throw Fx.Exception.ArgumentNull(nameof(collection)); _readOnlyIndicator = readOnlyIndicator; } #region ICollection<T> Members void ICollection<TKey>.Add(TKey item) { if (_readOnlyIndicator.IsReadOnly) { throw Fx.Exception.AsError(new NotSupportedException(Resources.ObjectIsReadOnly)); } _collection.Add(item); } void ICollection<TKey>.Clear() { if (_readOnlyIndicator.IsReadOnly) { throw Fx.Exception.AsError(new NotSupportedException(Resources.ObjectIsReadOnly)); } _collection.Clear(); } bool ICollection<TKey>.Contains(TKey item) { return _collection.Contains(item); } /// <summary> /// Copies the key collection to the specified array /// </summary> /// <param name="array">Destination array</param> /// <param name="arrayIndex">Starting index to copy to</param> public void CopyTo(TKey[] array, int arrayIndex) { _collection.CopyTo(array, arrayIndex); } /// <summary> /// The count of keys /// </summary> public int Count => _collection.Count; bool ICollection<TKey>.IsReadOnly => true; bool ICollection<TKey>.Remove(TKey item) { if (_readOnlyIndicator.IsReadOnly) { throw Fx.Exception.AsError(new NotSupportedException(Resources.ObjectIsReadOnly)); } return _collection.Remove(item); } #endregion ICollection<T> Members #region IEnumerable<T> Members /// <summary> /// Gets an enumerator /// </summary> /// <returns>The enumerator</returns> public IEnumerator<TKey> GetEnumerator() { return _collection.GetEnumerator(); } #endregion IEnumerable<T> Members #region IEnumerable Members IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)_collection).GetEnumerator(); } #endregion IEnumerable Members #region ICollection Members void ICollection.CopyTo(Array array, int index) { throw Fx.Exception.AsError(new NotImplementedException()); } bool ICollection.IsSynchronized => false; object ICollection.SyncRoot { get { if (_syncRoot == null) { var c = _collection as ICollection; if (c != null) { _syncRoot = c.SyncRoot; } else { Interlocked.CompareExchange<object>(ref _syncRoot, new object(), null); } } return _syncRoot; } } #endregion ICollection Members } /// <summary> /// A collection of dictionary values /// </summary> [DebuggerDisplay("Count = {Count}")] [Serializable] #pragma warning disable CA1034 // Nested types should not be visible public sealed class ValueCollection : ICollection<TValue>, ICollection #pragma warning restore CA1034 // Nested types should not be visible { private readonly ICollection<TValue> _collection; [NonSerialized] private object _syncRoot; [NonSerialized] private readonly IReadOnlyIndicator _readOnlyIndicator; internal ValueCollection(ICollection<TValue> collection, IReadOnlyIndicator readOnlyIndicator) { _collection = collection ?? throw Fx.Exception.ArgumentNull(nameof(collection)); _readOnlyIndicator = readOnlyIndicator; } #region ICollection<T> Members void ICollection<TValue>.Add(TValue item) { if (_readOnlyIndicator.IsReadOnly) { throw Fx.Exception.AsError(new NotSupportedException(Resources.ObjectIsReadOnly)); } _collection.Add(item); } void ICollection<TValue>.Clear() { if (_readOnlyIndicator.IsReadOnly) { throw Fx.Exception.AsError(new NotSupportedException(Resources.ObjectIsReadOnly)); } _collection.Clear(); } bool ICollection<TValue>.Contains(TValue item) { return _collection.Contains(item); } /// <summary> /// Copies the values to the specified array /// </summary> /// <param name="array">The destination array</param> /// <param name="arrayIndex">The starting index</param> public void CopyTo(TValue[] array, int arrayIndex) { _collection.CopyTo(array, arrayIndex); } /// <summary> /// The count of values in the collection /// </summary> public int Count => _collection.Count; bool ICollection<TValue>.IsReadOnly => true; bool ICollection<TValue>.Remove(TValue item) { if (_readOnlyIndicator.IsReadOnly) { throw Fx.Exception.AsError(new NotSupportedException(Resources.ObjectIsReadOnly)); } return _collection.Remove(item); } #endregion ICollection<T> Members #region IEnumerable<T> Members /// <summary> /// Gets an enumerator /// </summary> /// <returns>The enumerator</returns> public IEnumerator<TValue> GetEnumerator() { return _collection.GetEnumerator(); } #endregion IEnumerable<T> Members #region IEnumerable Members IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)_collection).GetEnumerator(); } #endregion IEnumerable Members #region ICollection Members void ICollection.CopyTo(Array array, int index) { throw Fx.Exception.AsError(new NotImplementedException()); } bool ICollection.IsSynchronized => false; object ICollection.SyncRoot { get { if (_syncRoot == null) { var c = _collection as ICollection; if (c != null) { _syncRoot = c.SyncRoot; } else { Interlocked.CompareExchange<object>(ref _syncRoot, new object(), null); } } return _syncRoot; } } #endregion ICollection Members } private class AlwaysReadOnlyIndicator : IReadOnlyIndicator { public bool IsReadOnly => true; } } internal interface IReadOnlyIndicator { bool IsReadOnly { get; } } }