using System; using System.Collections; using System.Collections.Generic; using System.Linq; namespace Vanara.Collections { /// A generic class that creates a read-only dictionary from a list and getter function. /// The type of keys in the dictionary. /// The type of values in the dictionary. public class GenericVirtualReadOnlyDictionary : VirtualReadOnlyDictionary { private readonly TryGetValueDelegate getValFunc; private readonly Func hasKeyFunc; /// Initializes a new instance of the class. /// The enumerated list of keys. /// The function used to get a value given a key. Called directly by TryGetValue. /// /// An optional function to directly determine if a key exists. If not supplied, the default implementation checks for equality on /// every value in . /// public GenericVirtualReadOnlyDictionary(IEnumerable keys, TryGetValueDelegate getValue, Func hasKey = null) { Keys = keys ?? throw new ArgumentNullException(nameof(keys)); getValFunc = getValue ?? throw new ArgumentNullException(nameof(getValue)); hasKeyFunc = hasKey ?? DefHasKey; } /// Delegate for the implementation of the method. /// The key whose value to get. /// /// When this method returns, the value associated with the specified key, if the key is found; otherwise, the default value for the /// type of the parameter. This parameter is passed uninitialized. /// /// /// if the contains an element with the key; otherwise, . /// public delegate bool TryGetValueDelegate(TKey key, out TValue value); /// Gets an enumerable collection that contains the keys in the read-only dictionary. /// An enumerable collection that contains the keys in the read-only dictionary. public override IEnumerable Keys { get; } /// Determines whether the contains an element with the specified key. /// The key to locate in the . /// /// if the contains an element with the key; otherwise, . /// public override bool ContainsKey(TKey key) => hasKeyFunc(key); /// Gets the value associated with the specified key. /// The key whose value to get. /// /// When this method returns, the value associated with the specified key, if the key is found; otherwise, the default value for the /// type of the parameter. This parameter is passed uninitialized. /// /// /// if the contains an element with the key; otherwise, . /// public override bool TryGetValue(TKey key, out TValue value) => getValFunc(key, out value); private bool DefHasKey(TKey k1) => Keys.Any(k2 => Equals(k1, k2)); } /// /// A generic base class for providing a dictionary that gets and sets its values using virtual method calls. Useful for exposing lookups /// into existing list environments like the file system, registry, service controller, etc. /// /// The type of keys in the dictionary. /// The type of values in the dictionary. public abstract class VirtualDictionary : IReadOnlyDictionary, IDictionary { /// Initializes a new instance of the class. /// if set to true makes the collection read-only. protected VirtualDictionary(bool readOnly) => IsReadOnly = readOnly; /// Gets a value indicating whether this instance is read only. /// if this instance is read only; otherwise, . public bool IsReadOnly { get; internal set; } /// Gets an containing the keys of the . /// An containing the keys of the object that implements . public abstract ICollection Keys { get; } /// Gets an containing the values in the . /// An containing the values in the object that implements . /// /// The order of the keys in the enumerable collection is unspecified, but the implementation must guarantee that the values are in /// the same order as the corresponding keys in the enumerable collection that is returned by the property. /// public virtual ICollection Values => Keys.Select(k => this[k]).ToList(); /// Gets the number of elements contained in the . /// The number of elements contained in the . public virtual int Count => Keys.Count; /// Gets an enumerable collection that contains the keys in the read-only dictionary. /// An enumerable collection that contains the keys in the read-only dictionary. /// /// The order of the keys in the enumerable collection is unspecified, but the implementation must guarantee that the keys are in the /// same order as the corresponding values in the enumerable collection that is returned by the property. /// IEnumerable IReadOnlyDictionary.Keys => Keys; /// Gets an containing the values in the . /// An containing the values in the object that implements . IEnumerable IReadOnlyDictionary.Values => Values; /// Gets the enumerated list of items. /// The enumerated list of items. protected virtual IEnumerable> Items => Keys.Select(k => new KeyValuePair(k, this[k])); /// Gets or sets the with the specified key. /// The . /// The key. /// The with the specified key. public virtual TValue this[TKey key] { get => TryGetValue(key, out var value) ? value : default; set => SetValue(key, value); } /// Removes all items from the . public virtual void Clear() { foreach (var key in Keys.ToList()) Remove(key); } /// Determines whether the contains an element with the specified key. /// The key to locate in the . /// /// if the contains an element with the key; otherwise, . /// public virtual bool ContainsKey(TKey key) => Keys.Contains(key); /// /// Copies the elements of the to an , starting /// at a particular index. /// /// /// The one-dimensional that is the destination of the elements copied from /// . The must have zero-based indexing. /// /// The zero-based index in at which copying begins. public virtual void CopyTo(KeyValuePair[] array, int arrayIndex) { var items = Items.ToArray(); Array.Copy(items, 0, array, arrayIndex, items.Length); } /// Returns an enumerator that iterates through the collection. /// An enumerator that can be used to iterate through the collection. public IEnumerator> GetEnumerator() => Items.GetEnumerator(); /// Removes the element with the specified key from the . /// The key of the element to remove. /// /// if the element is successfully removed; otherwise, . This method also returns false /// if key was not found in the original . /// public virtual bool Remove(TKey key) => throw new NotSupportedException(); /// Gets the value associated with the specified key. /// The key whose value to get. /// /// When this method returns, the value associated with the specified key, if the key is found; otherwise, the default value for the /// type of the parameter. This parameter is passed uninitialized. /// /// /// if the contains an element with the key; otherwise, . /// public abstract bool TryGetValue(TKey key, out TValue value); /// Adds an element with the provided key and value to the . /// The object to use as the key of the element to add. /// The object to use as the value of the element to add. void IDictionary.Add(TKey key, TValue value) => SetValue(key, value); /// Adds an item to the . /// The object to add to the . void ICollection>.Add(KeyValuePair item) => SetValue(item.Key, item.Value); /// Determines whether this instance contains the object. /// The object to locate in the . /// /// true if is found in the ; otherwise, false. /// bool ICollection>.Contains(KeyValuePair item) => ContainsKey(item.Key) && Equals(this[item.Key], item.Value); /// Returns an enumerator that iterates through a collection. /// An object that can be used to iterate through the collection. IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); /// /// Removes the first occurrence of a specific object from the . /// /// The object to remove from the . /// /// true if was successfully removed from the ; otherwise, false. This method also returns false if is not found in the original . /// bool ICollection>.Remove(KeyValuePair item) => ((ICollection>)this).Contains(item) && Remove(item.Key); /// /// Sets the value that is associated with the specified key. If the value for does not exist, this method /// should create it. If the value does exist, this method should update it to match . /// /// The key for which to set the data. /// The value to associate with the key. protected virtual void SetValue(TKey key, TValue value) => throw new NotSupportedException(); } /// /// A generic base class for providing a read-only dictionary that gets its values using virtual method calls. Useful for exposing /// lookups into existing list environments like the file system, registry, service controller, etc. /// /// The type of keys in the dictionary. /// The type of values in the dictionary. public abstract class VirtualReadOnlyDictionary : IReadOnlyDictionary { /// Gets an enumerable collection that contains the keys in the read-only dictionary. /// An enumerable collection that contains the keys in the read-only dictionary. /// /// The order of the keys in the enumerable collection is unspecified, but the implementation must guarantee that the keys are in the /// same order as the corresponding values in the enumerable collection that is returned by the property. /// public abstract IEnumerable Keys { get; } /// /// Gets an containing the values in the . /// An containing the values in the object that implements . public virtual IEnumerable Values => Keys.Select(k => this[k]).ToList(); /// Gets the number of elements contained in the . /// The number of elements contained in the . public virtual int Count => Keys.Count(); /// Gets the enumerated list of items. /// The enumerated list of items. protected IEnumerable> Items => Keys.Select(k => new KeyValuePair(k, this[k])); /// Gets the with the specified key. /// The element with the specified key. /// The key of the element to get. public virtual TValue this[TKey key] => TryGetValue(key, out var value) ? value : default; /// Determines whether the contains an element with the specified key. /// The key to locate in the . /// /// if the contains an element with the key; otherwise, . /// public abstract bool ContainsKey(TKey key); /// Returns an enumerator that iterates through the collection. /// An enumerator that can be used to iterate through the collection. public IEnumerator> GetEnumerator() => Items.GetEnumerator(); /// Gets the value associated with the specified key. /// The key whose value to get. /// /// When this method returns, the value associated with the specified key, if the key is found; otherwise, the default value for the /// type of the parameter. This parameter is passed uninitialized. /// /// /// if the contains an element with the key; otherwise, . /// public abstract bool TryGetValue(TKey key, out TValue value); /// Returns an enumerator that iterates through a collection. /// An object that can be used to iterate through the collection. IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } }