// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
// From https://github.com/dotnet/corefxlab/blob/master/src/System.Collections.Generic.MultiValueDictionary/System/Collections/Generic/MultiValueDictionary.cs
using System;
using System.Collections;
using System.Collections.Generic;
namespace BTCPayServer
{
///
/// A MultiValueDictionary can be viewed as a that allows multiple
/// values for any given unique key. While the MultiValueDictionary API is
/// mostly the same as that of a regular , there is a distinction
/// in that getting the value for a key returns a of values
/// rather than a single value associated with that key. Additionally,
/// there is functionality to allow adding or removing more than a single
/// value at once.
///
/// The MultiValueDictionary can also be viewed as an IReadOnlyDictionary<TKey,IReadOnlyCollection<TValue>t>
/// where the is abstracted from the view of the programmer.
///
/// For a read-only MultiValueDictionary.
///
/// The type of the key.
/// The type of the value.
public class MultiValueDictionary :
IReadOnlyDictionary>
{
#region Variables
/*======================================================================
** Variables
======================================================================*/
///
/// The private dictionary that this class effectively wraps around
///
private readonly Dictionary dictionary;
///
/// The function to construct a new
///
///
private Func> NewCollectionFactory = () => new List();
///
/// The current version of this MultiValueDictionary used to determine MultiValueDictionary modification
/// during enumeration
///
private int version;
#endregion
#region Constructors
/*======================================================================
** Constructors
======================================================================*/
///
/// Initializes a new instance of the
/// class that is empty, has the default initial capacity, and uses the default
/// for .
///
public MultiValueDictionary()
{
dictionary = new Dictionary();
}
///
/// Initializes a new instance of the class that is
/// empty, has the specified initial capacity, and uses the default
/// for .
///
/// Initial number of keys that the will allocate space for
/// capacity must be >= 0
public MultiValueDictionary(int capacity)
{
if (capacity < 0)
throw new ArgumentOutOfRangeException("capacity", "Properties.Resources.ArgumentOutOfRange_NeedNonNegNum");
dictionary = new Dictionary(capacity);
}
///
/// Initializes a new instance of the class
/// that is empty, has the default initial capacity, and uses the
/// specified .
///
/// Specified comparer to use for the s
/// If is set to null, then the default for is used.
public MultiValueDictionary(IEqualityComparer comparer)
{
dictionary = new Dictionary(comparer);
}
///
/// Initializes a new instance of the class
/// that is empty, has the specified initial capacity, and uses the
/// specified .
///
/// Initial number of keys that the will allocate space for
/// Specified comparer to use for the s
/// Capacity must be >= 0
/// If is set to null, then the default for is used.
public MultiValueDictionary(int capacity, IEqualityComparer comparer)
{
if (capacity < 0)
throw new ArgumentOutOfRangeException("capacity", "Properties.Resources.ArgumentOutOfRange_NeedNonNegNum");
dictionary = new Dictionary(capacity, comparer);
}
///
/// Initializes a new instance of the class that contains
/// elements copied from the specified IEnumerable<KeyValuePair<TKey, IReadOnlyCollection<TValue>>> and uses the
/// default for the type.
///
/// IEnumerable to copy elements into this from
/// enumerable must be non-null
public MultiValueDictionary(IEnumerable>> enumerable)
: this(enumerable, null)
{
}
///
/// Initializes a new instance of the class that contains
/// elements copied from the specified IEnumerable<KeyValuePair<TKey, IReadOnlyCollection<TValue>>> and uses the
/// specified .
///
/// IEnumerable to copy elements into this from
/// Specified comparer to use for the s
/// enumerable must be non-null
/// If is set to null, then the default for is used.
public MultiValueDictionary(IEnumerable>> enumerable, IEqualityComparer comparer)
{
if (enumerable == null)
throw new ArgumentNullException("enumerable");
dictionary = new Dictionary(comparer);
foreach (var pair in enumerable)
AddRange(pair.Key, pair.Value);
}
#endregion
#region Static Factories
/*======================================================================
** Static Factories
======================================================================*/
///
/// Creates a new new instance of the
/// class that is empty, has the default initial capacity, and uses the default
/// for . The
/// internal dictionary will use instances of the
/// class as its collection type.
///
///
/// The collection type that this
/// will contain in its internal dictionary.
///
/// A new with the specified
/// parameters.
/// must not have
/// IsReadOnly set to true by default.
///
/// Note that must implement
/// in addition to being constructable through new(). The collection returned from the constructor
/// must also not have IsReadOnly set to True by default.
///
public static MultiValueDictionary Create()
where TValueCollection : ICollection, new()
{
if (new TValueCollection().IsReadOnly)
throw new InvalidOperationException("Properties.Resources.Create_TValueCollectionReadOnly");
var multiValueDictionary = new MultiValueDictionary();
multiValueDictionary.NewCollectionFactory = () => new TValueCollection();
return multiValueDictionary;
}
///
/// Creates a new new instance of the
/// class that is empty, has the specified initial capacity, and uses the default
/// for . The
/// internal dictionary will use instances of the
/// class as its collection type.
///
///
/// The collection type that this
/// will contain in its internal dictionary.
///
/// Initial number of keys that the will allocate space for
/// A new with the specified
/// parameters.
/// Capacity must be >= 0
/// must not have
/// IsReadOnly set to true by default.
///
/// Note that must implement
/// in addition to being constructable through new(). The collection returned from the constructor
/// must also not have IsReadOnly set to True by default.
///
public static MultiValueDictionary Create(int capacity)
where TValueCollection : ICollection, new()
{
if (capacity < 0)
throw new ArgumentOutOfRangeException("capacity", "Properties.Resources.ArgumentOutOfRange_NeedNonNegNum");
if (new TValueCollection().IsReadOnly)
throw new InvalidOperationException("Properties.Resources.Create_TValueCollectionReadOnly");
var multiValueDictionary = new MultiValueDictionary(capacity);
multiValueDictionary.NewCollectionFactory = () => new TValueCollection();
return multiValueDictionary;
}
///
/// Creates a new new instance of the
/// class that is empty, has the default initial capacity, and uses the specified
/// for . The
/// internal dictionary will use instances of the
/// class as its collection type.
///
///
/// The collection type that this
/// will contain in its internal dictionary.
///
/// Specified comparer to use for the s
/// must not have
/// IsReadOnly set to true by default.
/// A new with the specified
/// parameters.
/// If is set to null, then the default for is used.
///
/// Note that must implement
/// in addition to being constructable through new(). The collection returned from the constructor
/// must also not have IsReadOnly set to True by default.
///
public static MultiValueDictionary Create(IEqualityComparer comparer)
where TValueCollection : ICollection, new()
{
if (new TValueCollection().IsReadOnly)
throw new InvalidOperationException("Properties.Resources.Create_TValueCollectionReadOnly");
var multiValueDictionary = new MultiValueDictionary(comparer);
multiValueDictionary.NewCollectionFactory = () => new TValueCollection();
return multiValueDictionary;
}
///
/// Creates a new new instance of the
/// class that is empty, has the specified initial capacity, and uses the specified
/// for . The
/// internal dictionary will use instances of the
/// class as its collection type.
///
///
/// The collection type that this
/// will contain in its internal dictionary.
///
/// Initial number of keys that the will allocate space for
/// Specified comparer to use for the s
/// A new with the specified
/// parameters.
/// must not have
/// IsReadOnly set to true by default.
/// Capacity must be >= 0
/// If is set to null, then the default for is used.
///
/// Note that must implement
/// in addition to being constructable through new(). The collection returned from the constructor
/// must also not have IsReadOnly set to True by default.
///
public static MultiValueDictionary Create(int capacity, IEqualityComparer comparer)
where TValueCollection : ICollection, new()
{
if (capacity < 0)
throw new ArgumentOutOfRangeException("capacity", "Properties.Resources.ArgumentOutOfRange_NeedNonNegNum");
if (new TValueCollection().IsReadOnly)
throw new InvalidOperationException("Properties.Resources.Create_TValueCollectionReadOnly");
var multiValueDictionary = new MultiValueDictionary(capacity, comparer);
multiValueDictionary.NewCollectionFactory = () => new TValueCollection();
return multiValueDictionary;
}
///
/// Initializes a new instance of the class that contains
/// elements copied from the specified IEnumerable<KeyValuePair<TKey, IReadOnlyCollection<TValue>>>
/// and uses the default for the type.
/// The internal dictionary will use instances of the
/// class as its collection type.
///
///
/// The collection type that this
/// will contain in its internal dictionary.
///
/// IEnumerable to copy elements into this from
/// A new with the specified
/// parameters.
/// must not have
/// IsReadOnly set to true by default.
/// enumerable must be non-null
///
/// Note that must implement
/// in addition to being constructable through new(). The collection returned from the constructor
/// must also not have IsReadOnly set to True by default.
///
public static MultiValueDictionary Create(IEnumerable>> enumerable)
where TValueCollection : ICollection, new()
{
if (enumerable == null)
throw new ArgumentNullException("enumerable");
if (new TValueCollection().IsReadOnly)
throw new InvalidOperationException("Properties.Resources.Create_TValueCollectionReadOnly");
var multiValueDictionary = new MultiValueDictionary();
multiValueDictionary.NewCollectionFactory = () => new TValueCollection();
foreach (var pair in enumerable)
multiValueDictionary.AddRange(pair.Key, pair.Value);
return multiValueDictionary;
}
///
/// Initializes a new instance of the class that contains
/// elements copied from the specified IEnumerable<KeyValuePair<TKey, IReadOnlyCollection<TValue>>>
/// and uses the specified for the type.
/// The internal dictionary will use instances of the
/// class as its collection type.
///
///
/// The collection type that this
/// will contain in its internal dictionary.
///
/// IEnumerable to copy elements into this from
/// Specified comparer to use for the s
/// A new with the specified
/// parameters.
/// must not have
/// IsReadOnly set to true by default.
/// enumerable must be non-null
/// If is set to null, then the default for is used.
///
/// Note that must implement
/// in addition to being constructable through new(). The collection returned from the constructor
/// must also not have IsReadOnly set to True by default.
///
public static MultiValueDictionary Create(IEnumerable>> enumerable, IEqualityComparer comparer)
where TValueCollection : ICollection, new()
{
if (enumerable == null)
throw new ArgumentNullException("enumerable");
if (new TValueCollection().IsReadOnly)
throw new InvalidOperationException("Properties.Resources.Create_TValueCollectionReadOnly");
var multiValueDictionary = new MultiValueDictionary(comparer);
multiValueDictionary.NewCollectionFactory = () => new TValueCollection();
foreach (var pair in enumerable)
multiValueDictionary.AddRange(pair.Key, pair.Value);
return multiValueDictionary;
}
#endregion
#region Static Factories with Func parameters
/*======================================================================
** Static Factories with Func parameters
======================================================================*/
///
/// Creates a new new instance of the
/// class that is empty, has the default initial capacity, and uses the default
/// for . The
/// internal dictionary will use instances of the
/// class as its collection type.
///
///
/// The collection type that this
/// will contain in its internal dictionary.
///
/// A function to create a new to use
/// in the internal dictionary store of this .
/// A new with the specified
/// parameters.
/// must create collections with
/// IsReadOnly set to true by default.
///
/// Note that must implement
/// in addition to being constructable through new(). The collection returned from the constructor
/// must also not have IsReadOnly set to True by default.
///
public static MultiValueDictionary Create(Func collectionFactory)
where TValueCollection : ICollection
{
if (collectionFactory().IsReadOnly)
throw new InvalidOperationException(("Properties.Resources.Create_TValueCollectionReadOnly"));
var multiValueDictionary = new MultiValueDictionary();
multiValueDictionary.NewCollectionFactory = (Func>)(Delegate)collectionFactory;
return multiValueDictionary;
}
///
/// Creates a new new instance of the
/// class that is empty, has the specified initial capacity, and uses the default
/// for . The
/// internal dictionary will use instances of the
/// class as its collection type.
///
///
/// The collection type that this
/// will contain in its internal dictionary.
///
/// Initial number of keys that the will allocate space for
/// A function to create a new to use
/// in the internal dictionary store of this .
/// A new with the specified
/// parameters.
/// Capacity must be >= 0
/// must create collections with
/// IsReadOnly set to true by default.
///
/// Note that must implement
/// in addition to being constructable through new(). The collection returned from the constructor
/// must also not have IsReadOnly set to True by default.
///
public static MultiValueDictionary Create(int capacity, Func collectionFactory)
where TValueCollection : ICollection
{
if (capacity < 0)
throw new ArgumentOutOfRangeException("capacity", "Properties.Resources.ArgumentOutOfRange_NeedNonNegNum");
if (collectionFactory().IsReadOnly)
throw new InvalidOperationException(("Properties.Resources.Create_TValueCollectionReadOnly"));
var multiValueDictionary = new MultiValueDictionary(capacity);
multiValueDictionary.NewCollectionFactory = (Func>)(Delegate)collectionFactory;
return multiValueDictionary;
}
///
/// Creates a new new instance of the
/// class that is empty, has the default initial capacity, and uses the specified
/// for . The
/// internal dictionary will use instances of the
/// class as its collection type.
///
///
/// The collection type that this
/// will contain in its internal dictionary.
///
/// Specified comparer to use for the s
/// A function to create a new to use
/// in the internal dictionary store of this .
/// must create collections with
/// IsReadOnly set to true by default.
/// A new with the specified
/// parameters.
/// If is set to null, then the default for is used.
///
/// Note that must implement
/// in addition to being constructable through new(). The collection returned from the constructor
/// must also not have IsReadOnly set to True by default.
///
public static MultiValueDictionary Create(IEqualityComparer comparer, Func collectionFactory)
where TValueCollection : ICollection
{
if (collectionFactory().IsReadOnly)
throw new InvalidOperationException(("Properties.Resources.Create_TValueCollectionReadOnly"));
var multiValueDictionary = new MultiValueDictionary(comparer);
multiValueDictionary.NewCollectionFactory = (Func>)(Delegate)collectionFactory;
return multiValueDictionary;
}
///
/// Creates a new new instance of the
/// class that is empty, has the specified initial capacity, and uses the specified
/// for . The
/// internal dictionary will use instances of the
/// class as its collection type.
///
///
/// The collection type that this
/// will contain in its internal dictionary.
///
/// Initial number of keys that the will allocate space for
/// Specified comparer to use for the s
/// A function to create a new to use
/// in the internal dictionary store of this .
/// A new with the specified
/// parameters.
/// must create collections with
/// IsReadOnly set to true by default.
/// Capacity must be >= 0
/// If is set to null, then the default for is used.
///
/// Note that must implement
/// in addition to being constructable through new(). The collection returned from the constructor
/// must also not have IsReadOnly set to True by default.
///
public static MultiValueDictionary Create(int capacity, IEqualityComparer comparer, Func collectionFactory)
where TValueCollection : ICollection
{
if (capacity < 0)
throw new ArgumentOutOfRangeException("capacity", "Properties.Resources.ArgumentOutOfRange_NeedNonNegNum");
if (collectionFactory().IsReadOnly)
throw new InvalidOperationException(("Properties.Resources.Create_TValueCollectionReadOnly"));
var multiValueDictionary = new MultiValueDictionary(capacity, comparer);
multiValueDictionary.NewCollectionFactory = (Func>)(Delegate)collectionFactory;
return multiValueDictionary;
}
///
/// Initializes a new instance of the class that contains
/// elements copied from the specified IEnumerable<KeyValuePair<TKey, IReadOnlyCollection<TValue>>>
/// and uses the default for the type.
/// The internal dictionary will use instances of the
/// class as its collection type.
///
///
/// The collection type that this
/// will contain in its internal dictionary.
///
/// IEnumerable to copy elements into this from
/// A function to create a new to use
/// in the internal dictionary store of this .
/// A new with the specified
/// parameters.
/// must create collections with
/// IsReadOnly set to true by default.
/// enumerable must be non-null
///
/// Note that must implement
/// in addition to being constructable through new(). The collection returned from the constructor
/// must also not have IsReadOnly set to True by default.
///
public static MultiValueDictionary Create(IEnumerable>> enumerable, Func collectionFactory)
where TValueCollection : ICollection
{
if (enumerable == null)
throw new ArgumentNullException("enumerable");
if (collectionFactory().IsReadOnly)
throw new InvalidOperationException(("Properties.Resources.Create_TValueCollectionReadOnly"));
var multiValueDictionary = new MultiValueDictionary();
multiValueDictionary.NewCollectionFactory = (Func>)(Delegate)collectionFactory;
foreach (var pair in enumerable)
multiValueDictionary.AddRange(pair.Key, pair.Value);
return multiValueDictionary;
}
///
/// Initializes a new instance of the class that contains
/// elements copied from the specified IEnumerable<KeyValuePair<TKey, IReadOnlyCollection<TValue>>>
/// and uses the specified for the type.
/// The internal dictionary will use instances of the
/// class as its collection type.
///
///
/// The collection type that this
/// will contain in its internal dictionary.
///
/// IEnumerable to copy elements into this from
/// Specified comparer to use for the s
/// A function to create a new to use
/// in the internal dictionary store of this .
/// A new with the specified
/// parameters.
/// must create collections with
/// IsReadOnly set to true by default.
/// enumerable must be non-null
/// If is set to null, then the default for is used.
///
/// Note that must implement
/// in addition to being constructable through new(). The collection returned from the constructor
/// must also not have IsReadOnly set to True by default.
///
public static MultiValueDictionary Create(IEnumerable>> enumerable, IEqualityComparer comparer, Func collectionFactory)
where TValueCollection : ICollection
{
if (enumerable == null)
throw new ArgumentNullException("enumerable");
if (collectionFactory().IsReadOnly)
throw new InvalidOperationException(("Properties.Resources.Create_TValueCollectionReadOnly"));
var multiValueDictionary = new MultiValueDictionary(comparer);
multiValueDictionary.NewCollectionFactory = (Func>)(Delegate)collectionFactory;
foreach (var pair in enumerable)
multiValueDictionary.AddRange(pair.Key, pair.Value);
return multiValueDictionary;
}
#endregion
#region Concrete Methods
/*======================================================================
** Concrete Methods
======================================================================*/
///
/// Adds the specified and to the .
///
/// The of the element to add.
/// The of the element to add.
/// is null.
///
/// Unlike the Add for , the Add will not
/// throw any exceptions. If the given is already in the ,
/// then will be added to associated with
///
///
/// A call to this Add method will always invalidate any currently running enumeration regardless
/// of whether the Add method actually modified the .
///
public void Add(TKey key, TValue value)
{
if (key == null)
throw new ArgumentNullException("key");
InnerCollectionView collection;
if (!dictionary.TryGetValue(key, out collection))
{
collection = new InnerCollectionView(key, NewCollectionFactory());
dictionary.Add(key, collection);
}
collection.AddValue(value);
version++;
}
///
/// Adds a number of key-value pairs to this , where
/// the key for each value is , and the value for a pair
/// is an element from
///
/// The of all entries to add
/// An of values to add
/// and must be non-null
///
/// A call to this AddRange method will always invalidate any currently running enumeration regardless
/// of whether the AddRange method actually modified the .
///
public void AddRange(TKey key, IEnumerable values)
{
if (key == null)
throw new ArgumentNullException("key");
if (values == null)
throw new ArgumentNullException("values");
InnerCollectionView collection;
if (!dictionary.TryGetValue(key, out collection))
{
collection = new InnerCollectionView(key, NewCollectionFactory());
dictionary.Add(key, collection);
}
foreach (TValue value in values)
{
collection.AddValue(value);
}
version++;
}
///
/// Removes every associated with the given
/// from the .
///
/// The of the elements to remove
/// true if the removal was successful; otherwise false
/// is null.
public bool Remove(TKey key)
{
if (key == null)
throw new ArgumentNullException("key");
InnerCollectionView collection;
if (dictionary.TryGetValue(key, out collection) && dictionary.Remove(key))
{
version++;
return true;
}
return false;
}
///
/// Removes the first instance (if any) of the given -
/// pair from this .
///
/// The of the element to remove
/// The of the element to remove
/// must be non-null
/// true if the removal was successful; otherwise false
///
/// If the being removed is the last one associated with its , then that
/// will be removed from the and its
/// associated will be freed as if a call to
/// had been made.
///
public bool Remove(TKey key, TValue value)
{
if (key == null)
throw new ArgumentNullException("key");
InnerCollectionView collection;
if (dictionary.TryGetValue(key, out collection) && collection.RemoveValue(value))
{
if (collection.Count == 0)
dictionary.Remove(key);
version++;
return true;
}
return false;
}
///
/// Determines if the given -
/// pair exists within this .
///
/// The of the element.
/// The of the element.
/// true if found; otherwise false
/// must be non-null
public bool Contains(TKey key, TValue value)
{
if (key == null)
throw new ArgumentNullException("key");
InnerCollectionView collection;
return (dictionary.TryGetValue(key, out collection) && collection.Contains(value));
}
///
/// Determines if the given exists within this .
///
/// A to search the for
/// true if the contains the ; otherwise false
public bool ContainsValue(TValue value)
{
foreach (InnerCollectionView sublist in dictionary.Values)
if (sublist.Contains(value))
return true;
return false;
}
///
/// Removes every and from this
/// .
///
public void Clear()
{
dictionary.Clear();
version++;
}
#endregion
#region Members implemented from IReadOnlyDictionary>
/*======================================================================
** Members implemented from IReadOnlyDictionary>
======================================================================*/
///
/// Determines if the given exists within this and has
/// at least one associated with it.
///
/// The to search the for
/// true if the contains the requested ;
/// otherwise false.
/// must be non-null
public bool ContainsKey(TKey key)
{
if (key == null)
throw new ArgumentNullException("key");
// Since modification to the MultiValueDictionary is only allowed through its own API, we
// can ensure that if a collection is in the internal dictionary then it must have at least one
// associated TValue, or else it would have been removed whenever its final TValue was removed.
return dictionary.ContainsKey(key);
}
///
/// Gets each in this that
/// has one or more associated .
///
///
/// An containing each
/// in this that has one or more associated
/// .
///
public IEnumerable Keys
{
get
{
return dictionary.Keys;
}
}
///
/// Attempts to get the associated with the given
/// and place it into .
///
/// The of the element to retrieve
///
/// When this method returns, contains the associated with the specified
/// if it is found; otherwise contains the default value of .
///
///
/// true if the contains an element with the specified
/// ; otherwise, false.
///
/// must be non-null
public bool TryGetValue(TKey key, out IReadOnlyCollection value)
{
if (key == null)
throw new ArgumentNullException("key");
InnerCollectionView collection;
var success = dictionary.TryGetValue(key, out collection);
value = collection;
return success;
}
///
/// Gets an enumerable of from this ,
/// where each is the collection of every associated
/// with a present in the .
///
/// An IEnumerable of each in this
///
public IEnumerable> Values
{
get
{
return dictionary.Values;
}
}
///
/// Get every associated with the given . If
/// is not found in this , will
/// throw a .
///
/// The of the elements to retrieve.
/// must be non-null
/// does not have any associated
/// s in this .
///
/// An containing every
/// associated with .
///
///
/// Note that the returned will change alongside any changes
/// to the
///
public IReadOnlyCollection this[TKey key]
{
get
{
if (key == null)
throw new ArgumentNullException("key");
InnerCollectionView collection;
if (dictionary.TryGetValue(key, out collection))
return collection;
else
throw new KeyNotFoundException();
}
}
///
/// Returns the number of s with one or more associated
/// in this .
///
/// The number of s in this .
public int Count
{
get
{
return dictionary.Count;
}
}
///
/// Get an Enumerator over the -
/// pairs in this .
///
/// an Enumerator over the -
/// pairs in this .
public IEnumerator>> GetEnumerator()
{
return new Enumerator(this);
}
IEnumerator IEnumerable.GetEnumerator()
{
return new Enumerator(this);
}
#endregion
///
/// The Enumerator class for a
/// that iterates over -
/// pairs.
///
private class Enumerator :
IEnumerator>>
{
private readonly MultiValueDictionary multiValueDictionary;
private readonly int version;
private KeyValuePair> current;
private Dictionary.Enumerator enumerator;
private enum EnumerationState { BeforeFirst, During, AfterLast };
private EnumerationState state;
///
/// Constructor for the enumerator
///
/// A MultiValueDictionary to iterate over
internal Enumerator(MultiValueDictionary multiValueDictionary)
{
this.multiValueDictionary = multiValueDictionary;
this.version = multiValueDictionary.version;
this.current = default(KeyValuePair>);
this.enumerator = multiValueDictionary.dictionary.GetEnumerator();
this.state = EnumerationState.BeforeFirst;
;
}
public KeyValuePair> Current
{
get
{
return current;
}
}
object IEnumerator.Current
{
get
{
switch (state)
{
case EnumerationState.BeforeFirst:
throw new InvalidOperationException(("Properties.Resources.InvalidOperation_EnumNotStarted"));
case EnumerationState.AfterLast:
throw new InvalidOperationException(("Properties.Resources.InvalidOperation_EnumEnded"));
default:
return current;
}
}
}
///
/// Advances the enumerator to the next element of the collection.
///
///
/// true if the enumerator was successfully advanced to the next element; false if the enumerator has passed the end of the collection.
///
/// The collection was modified after the enumerator was created.
public bool MoveNext()
{
if (version != multiValueDictionary.version)
{
throw new InvalidOperationException("Properties.Resources.InvalidOperation_EnumFailedVersion");
}
else if (enumerator.MoveNext())
{
current = new KeyValuePair>(enumerator.Current.Key, enumerator.Current.Value);
state = EnumerationState.During;
return true;
}
else
{
current = default(KeyValuePair>);
state = EnumerationState.AfterLast;
return false;
}
}
///
/// Sets the enumerator to its initial position, which is before the first element in the collection.
///
/// The collection was modified after the enumerator was created.
public void Reset()
{
if (version != multiValueDictionary.version)
throw new InvalidOperationException("Properties.Resources.InvalidOperation_EnumFailedVersion");
enumerator.Dispose();
enumerator = multiValueDictionary.dictionary.GetEnumerator();
current = default(KeyValuePair>);
state = EnumerationState.BeforeFirst;
}
///
/// Frees resources associated with this Enumerator
///
public void Dispose()
{
enumerator.Dispose();
}
}
///
/// An inner class that functions as a view of an ICollection within a MultiValueDictionary
///
private class InnerCollectionView :
ICollection,
IReadOnlyCollection
{
private readonly TKey key;
private readonly ICollection collection;
#region Private Concrete API
/*======================================================================
** Private Concrete API
======================================================================*/
public InnerCollectionView(TKey key, ICollection collection)
{
this.key = key;
this.collection = collection;
}
public void AddValue(TValue item)
{
collection.Add(item);
}
public bool RemoveValue(TValue item)
{
return collection.Remove(item);
}
#endregion
#region Shared API
/*======================================================================
** Shared API
======================================================================*/
public bool Contains(TValue item)
{
return collection.Contains(item);
}
public void CopyTo(TValue[] array, int arrayIndex)
{
if (array == null)
throw new ArgumentNullException("array");
if (arrayIndex < 0)
throw new ArgumentOutOfRangeException("arrayIndex", "Properties.Resources.ArgumentOutOfRange_NeedNonNegNum");
if (arrayIndex > array.Length)
throw new ArgumentOutOfRangeException("arrayIndex", "Properties.Resources.ArgumentOutOfRange_Index");
if (array.Length - arrayIndex < collection.Count)
throw new ArgumentException("Properties.Resources.CopyTo_ArgumentsTooSmall", "arrayIndex");
collection.CopyTo(array, arrayIndex);
}
public int Count
{
get
{
return collection.Count;
}
}
public bool IsReadOnly
{
get
{
return true;
}
}
public IEnumerator GetEnumerator()
{
return collection.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
public TKey Key
{
get
{
return key;
}
}
#endregion
#region Public-Facing API
/*======================================================================
** Public-Facing API
======================================================================*/
void ICollection.Add(TValue item)
{
throw new NotSupportedException("Properties.Resources.ReadOnly_Modification");
}
void ICollection.Clear()
{
throw new NotSupportedException("Properties.Resources.ReadOnly_Modification");
}
bool ICollection.Remove(TValue item)
{
throw new NotSupportedException("Properties.Resources.ReadOnly_Modification");
}
#endregion
}
}
public static class MultiValueDictionaryExtensions
{
public static MultiValueDictionary ToMultiValueDictionary(this IEnumerable collection, Func keySelector, Func valueSelector)
{
var dictionary = new MultiValueDictionary();
foreach (var item in collection)
{
dictionary.Add(keySelector(item), valueSelector(item));
}
return dictionary;
}
}
}