mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2024-11-19 18:11:36 +01:00
1128 lines
60 KiB
C#
1128 lines
60 KiB
C#
// 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
|
|
{
|
|
/// <summary>
|
|
/// A MultiValueDictionary can be viewed as a <see cref="IDictionary" /> that allows multiple
|
|
/// values for any given unique key. While the MultiValueDictionary API is
|
|
/// mostly the same as that of a regular <see cref="IDictionary" />, there is a distinction
|
|
/// in that getting the value for a key returns a <see cref="IReadOnlyCollection{TValue}" /> 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 <see cref="IReadOnlyCollection{TValue}" /> is abstracted from the view of the programmer.
|
|
///
|
|
/// For a read-only MultiValueDictionary.
|
|
/// </summary>
|
|
/// <typeparam name="TKey">The type of the key.</typeparam>
|
|
/// <typeparam name="TValue">The type of the value.</typeparam>
|
|
public class MultiValueDictionary<TKey, TValue> :
|
|
IReadOnlyDictionary<TKey, IReadOnlyCollection<TValue>>
|
|
{
|
|
#region Variables
|
|
/*======================================================================
|
|
** Variables
|
|
======================================================================*/
|
|
|
|
/// <summary>
|
|
/// The private dictionary that this class effectively wraps around
|
|
/// </summary>
|
|
private readonly Dictionary<TKey, InnerCollectionView> dictionary;
|
|
|
|
/// <summary>
|
|
/// The function to construct a new <see cref="ICollection{TValue}"/>
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
private Func<ICollection<TValue>> NewCollectionFactory = () => new List<TValue>();
|
|
|
|
/// <summary>
|
|
/// The current version of this MultiValueDictionary used to determine MultiValueDictionary modification
|
|
/// during enumeration
|
|
/// </summary>
|
|
private int version;
|
|
|
|
#endregion
|
|
|
|
#region Constructors
|
|
/*======================================================================
|
|
** Constructors
|
|
======================================================================*/
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="MultiValueDictionary{TKey, TValue}" />
|
|
/// class that is empty, has the default initial capacity, and uses the default
|
|
/// <see cref="IEqualityComparer{TKey}" /> for <typeparamref name="TKey"/>.
|
|
/// </summary>
|
|
public MultiValueDictionary()
|
|
{
|
|
dictionary = new Dictionary<TKey, InnerCollectionView>();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="MultiValueDictionary{TKey, TValue}" /> class that is
|
|
/// empty, has the specified initial capacity, and uses the default <see cref="IEqualityComparer{TKey}"/>
|
|
/// for <typeparamref name="TKey"/>.
|
|
/// </summary>
|
|
/// <param name="capacity">Initial number of keys that the <see cref="MultiValueDictionary{TKey, TValue}" /> will allocate space for</param>
|
|
/// <exception cref="ArgumentOutOfRangeException">capacity must be >= 0</exception>
|
|
public MultiValueDictionary(int capacity)
|
|
{
|
|
if (capacity < 0)
|
|
throw new ArgumentOutOfRangeException("capacity", "Properties.Resources.ArgumentOutOfRange_NeedNonNegNum");
|
|
dictionary = new Dictionary<TKey, InnerCollectionView>(capacity);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="MultiValueDictionary{TKey, TValue}" /> class
|
|
/// that is empty, has the default initial capacity, and uses the
|
|
/// specified <see cref="IEqualityComparer{TKey}" />.
|
|
/// </summary>
|
|
/// <param name="comparer">Specified comparer to use for the <typeparamref name="TKey"/>s</param>
|
|
/// <remarks>If <paramref name="comparer"/> is set to null, then the default <see cref="IEqualityComparer" /> for <typeparamref name="TKey"/> is used.</remarks>
|
|
public MultiValueDictionary(IEqualityComparer<TKey> comparer)
|
|
{
|
|
dictionary = new Dictionary<TKey, InnerCollectionView>(comparer);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="MultiValueDictionary{TKey, TValue}" /> class
|
|
/// that is empty, has the specified initial capacity, and uses the
|
|
/// specified <see cref="IEqualityComparer{TKey}" />.
|
|
/// </summary>
|
|
/// <param name="capacity">Initial number of keys that the <see cref="MultiValueDictionary{TKey, TValue}" /> will allocate space for</param>
|
|
/// <param name="comparer">Specified comparer to use for the <typeparamref name="TKey"/>s</param>
|
|
/// <exception cref="ArgumentOutOfRangeException">Capacity must be >= 0</exception>
|
|
/// <remarks>If <paramref name="comparer"/> is set to null, then the default <see cref="IEqualityComparer" /> for <typeparamref name="TKey"/> is used.</remarks>
|
|
public MultiValueDictionary(int capacity, IEqualityComparer<TKey> comparer)
|
|
{
|
|
if (capacity < 0)
|
|
throw new ArgumentOutOfRangeException("capacity", "Properties.Resources.ArgumentOutOfRange_NeedNonNegNum");
|
|
dictionary = new Dictionary<TKey, InnerCollectionView>(capacity, comparer);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="MultiValueDictionary{TKey, TValue}" /> class that contains
|
|
/// elements copied from the specified IEnumerable<KeyValuePair<TKey, IReadOnlyCollection<TValue>>> and uses the
|
|
/// default <see cref="IEqualityComparer{TKey}" /> for the <typeparamref name="TKey"/> type.
|
|
/// </summary>
|
|
/// <param name="enumerable">IEnumerable to copy elements into this from</param>
|
|
/// <exception cref="ArgumentNullException">enumerable must be non-null</exception>
|
|
public MultiValueDictionary(IEnumerable<KeyValuePair<TKey, IReadOnlyCollection<TValue>>> enumerable)
|
|
: this(enumerable, null)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="MultiValueDictionary{TKey, TValue}" /> class that contains
|
|
/// elements copied from the specified IEnumerable<KeyValuePair<TKey, IReadOnlyCollection<TValue>>> and uses the
|
|
/// specified <see cref="IEqualityComparer{TKey}" />.
|
|
/// </summary>
|
|
/// <param name="enumerable">IEnumerable to copy elements into this from</param>
|
|
/// <param name="comparer">Specified comparer to use for the <typeparamref name="TKey"/>s</param>
|
|
/// <exception cref="ArgumentNullException">enumerable must be non-null</exception>
|
|
/// <remarks>If <paramref name="comparer"/> is set to null, then the default <see cref="IEqualityComparer" /> for <typeparamref name="TKey"/> is used.</remarks>
|
|
public MultiValueDictionary(IEnumerable<KeyValuePair<TKey, IReadOnlyCollection<TValue>>> enumerable, IEqualityComparer<TKey> comparer)
|
|
{
|
|
if (enumerable == null)
|
|
throw new ArgumentNullException("enumerable");
|
|
|
|
dictionary = new Dictionary<TKey, InnerCollectionView>(comparer);
|
|
foreach (var pair in enumerable)
|
|
AddRange(pair.Key, pair.Value);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Static Factories
|
|
/*======================================================================
|
|
** Static Factories
|
|
======================================================================*/
|
|
|
|
/// <summary>
|
|
/// Creates a new new instance of the <see cref="MultiValueDictionary{TKey, TValue}" />
|
|
/// class that is empty, has the default initial capacity, and uses the default
|
|
/// <see cref="IEqualityComparer{TKey}" /> for <typeparamref name="TKey"/>. The
|
|
/// internal dictionary will use instances of the <typeparamref name="TValueCollection"/>
|
|
/// class as its collection type.
|
|
/// </summary>
|
|
/// <typeparam name="TValueCollection">
|
|
/// The collection type that this <see cref="MultiValueDictionary{TKey, TValue}" />
|
|
/// will contain in its internal dictionary.
|
|
/// </typeparam>
|
|
/// <returns>A new <see cref="MultiValueDictionary{TKey, TValue}" /> with the specified
|
|
/// parameters.</returns>
|
|
/// <exception cref="InvalidOperationException"><typeparamref name="TValueCollection"/> must not have
|
|
/// IsReadOnly set to true by default.</exception>
|
|
/// <remarks>
|
|
/// Note that <typeparamref name="TValueCollection"/> must implement <see cref="ICollection{TValue}"/>
|
|
/// in addition to being constructable through new(). The collection returned from the constructor
|
|
/// must also not have IsReadOnly set to True by default.
|
|
/// </remarks>
|
|
public static MultiValueDictionary<TKey, TValue> Create<TValueCollection>()
|
|
where TValueCollection : ICollection<TValue>, new()
|
|
{
|
|
if (new TValueCollection().IsReadOnly)
|
|
throw new InvalidOperationException("Properties.Resources.Create_TValueCollectionReadOnly");
|
|
|
|
var multiValueDictionary = new MultiValueDictionary<TKey, TValue>();
|
|
multiValueDictionary.NewCollectionFactory = () => new TValueCollection();
|
|
return multiValueDictionary;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a new new instance of the <see cref="MultiValueDictionary{TKey, TValue}" />
|
|
/// class that is empty, has the specified initial capacity, and uses the default
|
|
/// <see cref="IEqualityComparer{TKey}" /> for <typeparamref name="TKey"/>. The
|
|
/// internal dictionary will use instances of the <typeparamref name="TValueCollection"/>
|
|
/// class as its collection type.
|
|
/// </summary>
|
|
/// <typeparam name="TValueCollection">
|
|
/// The collection type that this <see cref="MultiValueDictionary{TKey, TValue}" />
|
|
/// will contain in its internal dictionary.
|
|
/// </typeparam>
|
|
/// <param name="capacity">Initial number of keys that the <see cref="MultiValueDictionary{TKey, TValue}" /> will allocate space for</param>
|
|
/// <returns>A new <see cref="MultiValueDictionary{TKey, TValue}" /> with the specified
|
|
/// parameters.</returns>
|
|
/// <exception cref="ArgumentOutOfRangeException">Capacity must be >= 0</exception>
|
|
/// <exception cref="InvalidOperationException"><typeparamref name="TValueCollection"/> must not have
|
|
/// IsReadOnly set to true by default.</exception>
|
|
/// <remarks>
|
|
/// Note that <typeparamref name="TValueCollection"/> must implement <see cref="ICollection{TValue}"/>
|
|
/// in addition to being constructable through new(). The collection returned from the constructor
|
|
/// must also not have IsReadOnly set to True by default.
|
|
/// </remarks>
|
|
public static MultiValueDictionary<TKey, TValue> Create<TValueCollection>(int capacity)
|
|
where TValueCollection : ICollection<TValue>, 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<TKey, TValue>(capacity);
|
|
multiValueDictionary.NewCollectionFactory = () => new TValueCollection();
|
|
return multiValueDictionary;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a new new instance of the <see cref="MultiValueDictionary{TKey, TValue}" />
|
|
/// class that is empty, has the default initial capacity, and uses the specified
|
|
/// <see cref="IEqualityComparer{TKey}" /> for <typeparamref name="TKey"/>. The
|
|
/// internal dictionary will use instances of the <typeparamref name="TValueCollection"/>
|
|
/// class as its collection type.
|
|
/// </summary>
|
|
/// <typeparam name="TValueCollection">
|
|
/// The collection type that this <see cref="MultiValueDictionary{TKey, TValue}" />
|
|
/// will contain in its internal dictionary.
|
|
/// </typeparam>
|
|
/// <param name="comparer">Specified comparer to use for the <typeparamref name="TKey"/>s</param>
|
|
/// <exception cref="InvalidOperationException"><typeparamref name="TValueCollection"/> must not have
|
|
/// IsReadOnly set to true by default.</exception>
|
|
/// <returns>A new <see cref="MultiValueDictionary{TKey, TValue}" /> with the specified
|
|
/// parameters.</returns>
|
|
/// <remarks>If <paramref name="comparer"/> is set to null, then the default <see cref="IEqualityComparer" /> for <typeparamref name="TKey"/> is used.</remarks>
|
|
/// <remarks>
|
|
/// Note that <typeparamref name="TValueCollection"/> must implement <see cref="ICollection{TValue}"/>
|
|
/// in addition to being constructable through new(). The collection returned from the constructor
|
|
/// must also not have IsReadOnly set to True by default.
|
|
/// </remarks>
|
|
public static MultiValueDictionary<TKey, TValue> Create<TValueCollection>(IEqualityComparer<TKey> comparer)
|
|
where TValueCollection : ICollection<TValue>, new()
|
|
{
|
|
if (new TValueCollection().IsReadOnly)
|
|
throw new InvalidOperationException("Properties.Resources.Create_TValueCollectionReadOnly");
|
|
|
|
var multiValueDictionary = new MultiValueDictionary<TKey, TValue>(comparer);
|
|
multiValueDictionary.NewCollectionFactory = () => new TValueCollection();
|
|
return multiValueDictionary;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a new new instance of the <see cref="MultiValueDictionary{TKey, TValue}" />
|
|
/// class that is empty, has the specified initial capacity, and uses the specified
|
|
/// <see cref="IEqualityComparer{TKey}" /> for <typeparamref name="TKey"/>. The
|
|
/// internal dictionary will use instances of the <typeparamref name="TValueCollection"/>
|
|
/// class as its collection type.
|
|
/// </summary>
|
|
/// <typeparam name="TValueCollection">
|
|
/// The collection type that this <see cref="MultiValueDictionary{TKey, TValue}" />
|
|
/// will contain in its internal dictionary.
|
|
/// </typeparam>
|
|
/// <param name="capacity">Initial number of keys that the <see cref="MultiValueDictionary{TKey, TValue}" /> will allocate space for</param>
|
|
/// <param name="comparer">Specified comparer to use for the <typeparamref name="TKey"/>s</param>
|
|
/// <returns>A new <see cref="MultiValueDictionary{TKey, TValue}" /> with the specified
|
|
/// parameters.</returns>
|
|
/// <exception cref="InvalidOperationException"><typeparamref name="TValueCollection"/> must not have
|
|
/// IsReadOnly set to true by default.</exception>
|
|
/// <exception cref="ArgumentOutOfRangeException">Capacity must be >= 0</exception>
|
|
/// <remarks>If <paramref name="comparer"/> is set to null, then the default <see cref="IEqualityComparer" /> for <typeparamref name="TKey"/> is used.</remarks>
|
|
/// <remarks>
|
|
/// Note that <typeparamref name="TValueCollection"/> must implement <see cref="ICollection{TValue}"/>
|
|
/// in addition to being constructable through new(). The collection returned from the constructor
|
|
/// must also not have IsReadOnly set to True by default.
|
|
/// </remarks>
|
|
public static MultiValueDictionary<TKey, TValue> Create<TValueCollection>(int capacity, IEqualityComparer<TKey> comparer)
|
|
where TValueCollection : ICollection<TValue>, 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<TKey, TValue>(capacity, comparer);
|
|
multiValueDictionary.NewCollectionFactory = () => new TValueCollection();
|
|
return multiValueDictionary;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="MultiValueDictionary{TKey, TValue}" /> class that contains
|
|
/// elements copied from the specified IEnumerable<KeyValuePair<TKey, IReadOnlyCollection<TValue>>>
|
|
/// and uses the default <see cref="IEqualityComparer{TKey}" /> for the <typeparamref name="TKey"/> type.
|
|
/// The internal dictionary will use instances of the <typeparamref name="TValueCollection"/>
|
|
/// class as its collection type.
|
|
/// </summary>
|
|
/// <typeparam name="TValueCollection">
|
|
/// The collection type that this <see cref="MultiValueDictionary{TKey, TValue}" />
|
|
/// will contain in its internal dictionary.
|
|
/// </typeparam>
|
|
/// <param name="enumerable">IEnumerable to copy elements into this from</param>
|
|
/// <returns>A new <see cref="MultiValueDictionary{TKey, TValue}" /> with the specified
|
|
/// parameters.</returns>
|
|
/// <exception cref="InvalidOperationException"><typeparamref name="TValueCollection"/> must not have
|
|
/// IsReadOnly set to true by default.</exception>
|
|
/// <exception cref="ArgumentNullException">enumerable must be non-null</exception>
|
|
/// <remarks>
|
|
/// Note that <typeparamref name="TValueCollection"/> must implement <see cref="ICollection{TValue}"/>
|
|
/// in addition to being constructable through new(). The collection returned from the constructor
|
|
/// must also not have IsReadOnly set to True by default.
|
|
/// </remarks>
|
|
public static MultiValueDictionary<TKey, TValue> Create<TValueCollection>(IEnumerable<KeyValuePair<TKey, IReadOnlyCollection<TValue>>> enumerable)
|
|
where TValueCollection : ICollection<TValue>, new()
|
|
{
|
|
if (enumerable == null)
|
|
throw new ArgumentNullException("enumerable");
|
|
if (new TValueCollection().IsReadOnly)
|
|
throw new InvalidOperationException("Properties.Resources.Create_TValueCollectionReadOnly");
|
|
|
|
var multiValueDictionary = new MultiValueDictionary<TKey, TValue>();
|
|
multiValueDictionary.NewCollectionFactory = () => new TValueCollection();
|
|
foreach (var pair in enumerable)
|
|
multiValueDictionary.AddRange(pair.Key, pair.Value);
|
|
return multiValueDictionary;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="MultiValueDictionary{TKey, TValue}" /> class that contains
|
|
/// elements copied from the specified IEnumerable<KeyValuePair<TKey, IReadOnlyCollection<TValue>>>
|
|
/// and uses the specified <see cref="IEqualityComparer{TKey}" /> for the <typeparamref name="TKey"/> type.
|
|
/// The internal dictionary will use instances of the <typeparamref name="TValueCollection"/>
|
|
/// class as its collection type.
|
|
/// </summary>
|
|
/// <typeparam name="TValueCollection">
|
|
/// The collection type that this <see cref="MultiValueDictionary{TKey, TValue}" />
|
|
/// will contain in its internal dictionary.
|
|
/// </typeparam>
|
|
/// <param name="enumerable">IEnumerable to copy elements into this from</param>
|
|
/// <param name="comparer">Specified comparer to use for the <typeparamref name="TKey"/>s</param>
|
|
/// <returns>A new <see cref="MultiValueDictionary{TKey, TValue}" /> with the specified
|
|
/// parameters.</returns>
|
|
/// <exception cref="InvalidOperationException"><typeparamref name="TValueCollection"/> must not have
|
|
/// IsReadOnly set to true by default.</exception>
|
|
/// <exception cref="ArgumentNullException">enumerable must be non-null</exception>
|
|
/// <remarks>If <paramref name="comparer"/> is set to null, then the default <see cref="IEqualityComparer" /> for <typeparamref name="TKey"/> is used.</remarks>
|
|
/// <remarks>
|
|
/// Note that <typeparamref name="TValueCollection"/> must implement <see cref="ICollection{TValue}"/>
|
|
/// in addition to being constructable through new(). The collection returned from the constructor
|
|
/// must also not have IsReadOnly set to True by default.
|
|
/// </remarks>
|
|
public static MultiValueDictionary<TKey, TValue> Create<TValueCollection>(IEnumerable<KeyValuePair<TKey, IReadOnlyCollection<TValue>>> enumerable, IEqualityComparer<TKey> comparer)
|
|
where TValueCollection : ICollection<TValue>, new()
|
|
{
|
|
if (enumerable == null)
|
|
throw new ArgumentNullException("enumerable");
|
|
if (new TValueCollection().IsReadOnly)
|
|
throw new InvalidOperationException("Properties.Resources.Create_TValueCollectionReadOnly");
|
|
|
|
var multiValueDictionary = new MultiValueDictionary<TKey, TValue>(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
|
|
======================================================================*/
|
|
|
|
/// <summary>
|
|
/// Creates a new new instance of the <see cref="MultiValueDictionary{TKey, TValue}" />
|
|
/// class that is empty, has the default initial capacity, and uses the default
|
|
/// <see cref="IEqualityComparer{TKey}" /> for <typeparamref name="TKey"/>. The
|
|
/// internal dictionary will use instances of the <typeparamref name="TValueCollection"/>
|
|
/// class as its collection type.
|
|
/// </summary>
|
|
/// <typeparam name="TValueCollection">
|
|
/// The collection type that this <see cref="MultiValueDictionary{TKey, TValue}" />
|
|
/// will contain in its internal dictionary.
|
|
/// </typeparam>
|
|
/// <param name="collectionFactory">A function to create a new <see cref="ICollection{TValue}"/> to use
|
|
/// in the internal dictionary store of this <see cref="MultiValueDictionary{TKey, TValue}" />.</param>
|
|
/// <returns>A new <see cref="MultiValueDictionary{TKey, TValue}" /> with the specified
|
|
/// parameters.</returns>
|
|
/// <exception cref="InvalidOperationException"><paramref name="collectionFactory"/> must create collections with
|
|
/// IsReadOnly set to true by default.</exception>
|
|
/// <remarks>
|
|
/// Note that <typeparamref name="TValueCollection"/> must implement <see cref="ICollection{TValue}"/>
|
|
/// in addition to being constructable through new(). The collection returned from the constructor
|
|
/// must also not have IsReadOnly set to True by default.
|
|
/// </remarks>
|
|
public static MultiValueDictionary<TKey, TValue> Create<TValueCollection>(Func<TValueCollection> collectionFactory)
|
|
where TValueCollection : ICollection<TValue>
|
|
{
|
|
if (collectionFactory().IsReadOnly)
|
|
throw new InvalidOperationException(("Properties.Resources.Create_TValueCollectionReadOnly"));
|
|
|
|
var multiValueDictionary = new MultiValueDictionary<TKey, TValue>();
|
|
multiValueDictionary.NewCollectionFactory = (Func<ICollection<TValue>>)(Delegate)collectionFactory;
|
|
return multiValueDictionary;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a new new instance of the <see cref="MultiValueDictionary{TKey, TValue}" />
|
|
/// class that is empty, has the specified initial capacity, and uses the default
|
|
/// <see cref="IEqualityComparer{TKey}" /> for <typeparamref name="TKey"/>. The
|
|
/// internal dictionary will use instances of the <typeparamref name="TValueCollection"/>
|
|
/// class as its collection type.
|
|
/// </summary>
|
|
/// <typeparam name="TValueCollection">
|
|
/// The collection type that this <see cref="MultiValueDictionary{TKey, TValue}" />
|
|
/// will contain in its internal dictionary.
|
|
/// </typeparam>
|
|
/// <param name="capacity">Initial number of keys that the <see cref="MultiValueDictionary{TKey, TValue}" /> will allocate space for</param>
|
|
/// <param name="collectionFactory">A function to create a new <see cref="ICollection{TValue}"/> to use
|
|
/// in the internal dictionary store of this <see cref="MultiValueDictionary{TKey, TValue}" />.</param>
|
|
/// <returns>A new <see cref="MultiValueDictionary{TKey, TValue}" /> with the specified
|
|
/// parameters.</returns>
|
|
/// <exception cref="ArgumentOutOfRangeException">Capacity must be >= 0</exception>
|
|
/// <exception cref="InvalidOperationException"><paramref name="collectionFactory"/> must create collections with
|
|
/// IsReadOnly set to true by default.</exception>
|
|
/// <remarks>
|
|
/// Note that <typeparamref name="TValueCollection"/> must implement <see cref="ICollection{TValue}"/>
|
|
/// in addition to being constructable through new(). The collection returned from the constructor
|
|
/// must also not have IsReadOnly set to True by default.
|
|
/// </remarks>
|
|
public static MultiValueDictionary<TKey, TValue> Create<TValueCollection>(int capacity, Func<TValueCollection> collectionFactory)
|
|
where TValueCollection : ICollection<TValue>
|
|
{
|
|
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<TKey, TValue>(capacity);
|
|
multiValueDictionary.NewCollectionFactory = (Func<ICollection<TValue>>)(Delegate)collectionFactory;
|
|
return multiValueDictionary;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a new new instance of the <see cref="MultiValueDictionary{TKey, TValue}" />
|
|
/// class that is empty, has the default initial capacity, and uses the specified
|
|
/// <see cref="IEqualityComparer{TKey}" /> for <typeparamref name="TKey"/>. The
|
|
/// internal dictionary will use instances of the <typeparamref name="TValueCollection"/>
|
|
/// class as its collection type.
|
|
/// </summary>
|
|
/// <typeparam name="TValueCollection">
|
|
/// The collection type that this <see cref="MultiValueDictionary{TKey, TValue}" />
|
|
/// will contain in its internal dictionary.
|
|
/// </typeparam>
|
|
/// <param name="comparer">Specified comparer to use for the <typeparamref name="TKey"/>s</param>
|
|
/// <param name="collectionFactory">A function to create a new <see cref="ICollection{TValue}"/> to use
|
|
/// in the internal dictionary store of this <see cref="MultiValueDictionary{TKey, TValue}" />.</param>
|
|
/// <exception cref="InvalidOperationException"><paramref name="collectionFactory"/> must create collections with
|
|
/// IsReadOnly set to true by default.</exception>
|
|
/// <returns>A new <see cref="MultiValueDictionary{TKey, TValue}" /> with the specified
|
|
/// parameters.</returns>
|
|
/// <remarks>If <paramref name="comparer"/> is set to null, then the default <see cref="IEqualityComparer" /> for <typeparamref name="TKey"/> is used.</remarks>
|
|
/// <remarks>
|
|
/// Note that <typeparamref name="TValueCollection"/> must implement <see cref="ICollection{TValue}"/>
|
|
/// in addition to being constructable through new(). The collection returned from the constructor
|
|
/// must also not have IsReadOnly set to True by default.
|
|
/// </remarks>
|
|
public static MultiValueDictionary<TKey, TValue> Create<TValueCollection>(IEqualityComparer<TKey> comparer, Func<TValueCollection> collectionFactory)
|
|
where TValueCollection : ICollection<TValue>
|
|
{
|
|
if (collectionFactory().IsReadOnly)
|
|
throw new InvalidOperationException(("Properties.Resources.Create_TValueCollectionReadOnly"));
|
|
|
|
var multiValueDictionary = new MultiValueDictionary<TKey, TValue>(comparer);
|
|
multiValueDictionary.NewCollectionFactory = (Func<ICollection<TValue>>)(Delegate)collectionFactory;
|
|
return multiValueDictionary;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a new new instance of the <see cref="MultiValueDictionary{TKey, TValue}" />
|
|
/// class that is empty, has the specified initial capacity, and uses the specified
|
|
/// <see cref="IEqualityComparer{TKey}" /> for <typeparamref name="TKey"/>. The
|
|
/// internal dictionary will use instances of the <typeparamref name="TValueCollection"/>
|
|
/// class as its collection type.
|
|
/// </summary>
|
|
/// <typeparam name="TValueCollection">
|
|
/// The collection type that this <see cref="MultiValueDictionary{TKey, TValue}" />
|
|
/// will contain in its internal dictionary.
|
|
/// </typeparam>
|
|
/// <param name="capacity">Initial number of keys that the <see cref="MultiValueDictionary{TKey, TValue}" /> will allocate space for</param>
|
|
/// <param name="comparer">Specified comparer to use for the <typeparamref name="TKey"/>s</param>
|
|
/// <param name="collectionFactory">A function to create a new <see cref="ICollection{TValue}"/> to use
|
|
/// in the internal dictionary store of this <see cref="MultiValueDictionary{TKey, TValue}" />.</param>
|
|
/// <returns>A new <see cref="MultiValueDictionary{TKey, TValue}" /> with the specified
|
|
/// parameters.</returns>
|
|
/// <exception cref="InvalidOperationException"><paramref name="collectionFactory"/> must create collections with
|
|
/// IsReadOnly set to true by default.</exception>
|
|
/// <exception cref="ArgumentOutOfRangeException">Capacity must be >= 0</exception>
|
|
/// <remarks>If <paramref name="comparer"/> is set to null, then the default <see cref="IEqualityComparer" /> for <typeparamref name="TKey"/> is used.</remarks>
|
|
/// <remarks>
|
|
/// Note that <typeparamref name="TValueCollection"/> must implement <see cref="ICollection{TValue}"/>
|
|
/// in addition to being constructable through new(). The collection returned from the constructor
|
|
/// must also not have IsReadOnly set to True by default.
|
|
/// </remarks>
|
|
public static MultiValueDictionary<TKey, TValue> Create<TValueCollection>(int capacity, IEqualityComparer<TKey> comparer, Func<TValueCollection> collectionFactory)
|
|
where TValueCollection : ICollection<TValue>
|
|
{
|
|
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<TKey, TValue>(capacity, comparer);
|
|
multiValueDictionary.NewCollectionFactory = (Func<ICollection<TValue>>)(Delegate)collectionFactory;
|
|
return multiValueDictionary;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="MultiValueDictionary{TKey, TValue}" /> class that contains
|
|
/// elements copied from the specified IEnumerable<KeyValuePair<TKey, IReadOnlyCollection<TValue>>>
|
|
/// and uses the default <see cref="IEqualityComparer{TKey}" /> for the <typeparamref name="TKey"/> type.
|
|
/// The internal dictionary will use instances of the <typeparamref name="TValueCollection"/>
|
|
/// class as its collection type.
|
|
/// </summary>
|
|
/// <typeparam name="TValueCollection">
|
|
/// The collection type that this <see cref="MultiValueDictionary{TKey, TValue}" />
|
|
/// will contain in its internal dictionary.
|
|
/// </typeparam>
|
|
/// <param name="enumerable">IEnumerable to copy elements into this from</param>
|
|
/// <param name="collectionFactory">A function to create a new <see cref="ICollection{TValue}"/> to use
|
|
/// in the internal dictionary store of this <see cref="MultiValueDictionary{TKey, TValue}" />.</param>
|
|
/// <returns>A new <see cref="MultiValueDictionary{TKey, TValue}" /> with the specified
|
|
/// parameters.</returns>
|
|
/// <exception cref="InvalidOperationException"><paramref name="collectionFactory"/> must create collections with
|
|
/// IsReadOnly set to true by default.</exception>
|
|
/// <exception cref="ArgumentNullException">enumerable must be non-null</exception>
|
|
/// <remarks>
|
|
/// Note that <typeparamref name="TValueCollection"/> must implement <see cref="ICollection{TValue}"/>
|
|
/// in addition to being constructable through new(). The collection returned from the constructor
|
|
/// must also not have IsReadOnly set to True by default.
|
|
/// </remarks>
|
|
public static MultiValueDictionary<TKey, TValue> Create<TValueCollection>(IEnumerable<KeyValuePair<TKey, IReadOnlyCollection<TValue>>> enumerable, Func<TValueCollection> collectionFactory)
|
|
where TValueCollection : ICollection<TValue>
|
|
{
|
|
if (enumerable == null)
|
|
throw new ArgumentNullException("enumerable");
|
|
if (collectionFactory().IsReadOnly)
|
|
throw new InvalidOperationException(("Properties.Resources.Create_TValueCollectionReadOnly"));
|
|
|
|
var multiValueDictionary = new MultiValueDictionary<TKey, TValue>();
|
|
multiValueDictionary.NewCollectionFactory = (Func<ICollection<TValue>>)(Delegate)collectionFactory;
|
|
foreach (var pair in enumerable)
|
|
multiValueDictionary.AddRange(pair.Key, pair.Value);
|
|
return multiValueDictionary;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="MultiValueDictionary{TKey, TValue}" /> class that contains
|
|
/// elements copied from the specified IEnumerable<KeyValuePair<TKey, IReadOnlyCollection<TValue>>>
|
|
/// and uses the specified <see cref="IEqualityComparer{TKey}" /> for the <typeparamref name="TKey"/> type.
|
|
/// The internal dictionary will use instances of the <typeparamref name="TValueCollection"/>
|
|
/// class as its collection type.
|
|
/// </summary>
|
|
/// <typeparam name="TValueCollection">
|
|
/// The collection type that this <see cref="MultiValueDictionary{TKey, TValue}" />
|
|
/// will contain in its internal dictionary.
|
|
/// </typeparam>
|
|
/// <param name="enumerable">IEnumerable to copy elements into this from</param>
|
|
/// <param name="comparer">Specified comparer to use for the <typeparamref name="TKey"/>s</param>
|
|
/// <param name="collectionFactory">A function to create a new <see cref="ICollection{TValue}"/> to use
|
|
/// in the internal dictionary store of this <see cref="MultiValueDictionary{TKey, TValue}" />.</param>
|
|
/// <returns>A new <see cref="MultiValueDictionary{TKey, TValue}" /> with the specified
|
|
/// parameters.</returns>
|
|
/// <exception cref="InvalidOperationException"><paramref name="collectionFactory"/> must create collections with
|
|
/// IsReadOnly set to true by default.</exception>
|
|
/// <exception cref="ArgumentNullException">enumerable must be non-null</exception>
|
|
/// <remarks>If <paramref name="comparer"/> is set to null, then the default <see cref="IEqualityComparer" /> for <typeparamref name="TKey"/> is used.</remarks>
|
|
/// <remarks>
|
|
/// Note that <typeparamref name="TValueCollection"/> must implement <see cref="ICollection{TValue}"/>
|
|
/// in addition to being constructable through new(). The collection returned from the constructor
|
|
/// must also not have IsReadOnly set to True by default.
|
|
/// </remarks>
|
|
public static MultiValueDictionary<TKey, TValue> Create<TValueCollection>(IEnumerable<KeyValuePair<TKey, IReadOnlyCollection<TValue>>> enumerable, IEqualityComparer<TKey> comparer, Func<TValueCollection> collectionFactory)
|
|
where TValueCollection : ICollection<TValue>
|
|
{
|
|
if (enumerable == null)
|
|
throw new ArgumentNullException("enumerable");
|
|
if (collectionFactory().IsReadOnly)
|
|
throw new InvalidOperationException(("Properties.Resources.Create_TValueCollectionReadOnly"));
|
|
|
|
var multiValueDictionary = new MultiValueDictionary<TKey, TValue>(comparer);
|
|
multiValueDictionary.NewCollectionFactory = (Func<ICollection<TValue>>)(Delegate)collectionFactory;
|
|
foreach (var pair in enumerable)
|
|
multiValueDictionary.AddRange(pair.Key, pair.Value);
|
|
return multiValueDictionary;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Concrete Methods
|
|
/*======================================================================
|
|
** Concrete Methods
|
|
======================================================================*/
|
|
|
|
/// <summary>
|
|
/// Adds the specified <typeparamref name="TKey"/> and <typeparamref name="TValue"/> to the <see cref="MultiValueDictionary{TKey,TValue}"/>.
|
|
/// </summary>
|
|
/// <param name="key">The <typeparamref name="TKey"/> of the element to add.</param>
|
|
/// <param name="value">The <typeparamref name="TValue"/> of the element to add.</param>
|
|
/// <exception cref="ArgumentNullException"><paramref name="key"/> is <c>null</c>.</exception>
|
|
/// <remarks>
|
|
/// Unlike the Add for <see cref="IDictionary" />, the <see cref="MultiValueDictionary{TKey,TValue}"/> Add will not
|
|
/// throw any exceptions. If the given <typeparamref name="TKey"/> is already in the <see cref="MultiValueDictionary{TKey,TValue}"/>,
|
|
/// then <typeparamref name="TValue"/> will be added to <see cref="IReadOnlyCollection{TValue}"/> associated with <paramref name="key"/>
|
|
/// </remarks>
|
|
/// <remarks>
|
|
/// A call to this Add method will always invalidate any currently running enumeration regardless
|
|
/// of whether the Add method actually modified the <see cref="MultiValueDictionary{TKey, TValue}" />.
|
|
/// </remarks>
|
|
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++;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds a number of key-value pairs to this <see cref="MultiValueDictionary{TKey,TValue}"/>, where
|
|
/// the key for each value is <paramref name="key"/>, and the value for a pair
|
|
/// is an element from <paramref name="values"/>
|
|
/// </summary>
|
|
/// <param name="key">The <typeparamref name="TKey"/> of all entries to add</param>
|
|
/// <param name="values">An <see cref="IEnumerable{TValue}"/> of values to add</param>
|
|
/// <exception cref="ArgumentNullException"><paramref name="key"/> and <paramref name="values"/> must be non-null</exception>
|
|
/// <remarks>
|
|
/// A call to this AddRange method will always invalidate any currently running enumeration regardless
|
|
/// of whether the AddRange method actually modified the <see cref="MultiValueDictionary{TKey,TValue}"/>.
|
|
/// </remarks>
|
|
public void AddRange(TKey key, IEnumerable<TValue> 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++;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes every <typeparamref name="TValue"/> associated with the given <typeparamref name="TKey"/>
|
|
/// from the <see cref="MultiValueDictionary{TKey,TValue}"/>.
|
|
/// </summary>
|
|
/// <param name="key">The <typeparamref name="TKey"/> of the elements to remove</param>
|
|
/// <returns><c>true</c> if the removal was successful; otherwise <c>false</c></returns>
|
|
/// <exception cref="ArgumentNullException"><paramref name="key"/> is <c>null</c>.</exception>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes the first instance (if any) of the given <typeparamref name="TKey"/>-<typeparamref name="TValue"/>
|
|
/// pair from this <see cref="MultiValueDictionary{TKey,TValue}"/>.
|
|
/// </summary>
|
|
/// <param name="key">The <typeparamref name="TKey"/> of the element to remove</param>
|
|
/// <param name="value">The <typeparamref name="TValue"/> of the element to remove</param>
|
|
/// <exception cref="ArgumentNullException"><paramref name="key"/> must be non-null</exception>
|
|
/// <returns><c>true</c> if the removal was successful; otherwise <c>false</c></returns>
|
|
/// <remarks>
|
|
/// If the <typeparamref name="TValue"/> being removed is the last one associated with its <typeparamref name="TKey"/>, then that
|
|
/// <typeparamref name="TKey"/> will be removed from the <see cref="MultiValueDictionary{TKey,TValue}"/> and its
|
|
/// associated <see cref="IReadOnlyCollection{TValue}"/> will be freed as if a call to <see cref="Remove(TKey)"/>
|
|
/// had been made.
|
|
/// </remarks>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines if the given <typeparamref name="TKey"/>-<typeparamref name="TValue"/>
|
|
/// pair exists within this <see cref="MultiValueDictionary{TKey,TValue}"/>.
|
|
/// </summary>
|
|
/// <param name="key">The <typeparamref name="TKey"/> of the element.</param>
|
|
/// <param name="value">The <typeparamref name="TValue"/> of the element.</param>
|
|
/// <returns><c>true</c> if found; otherwise <c>false</c></returns>
|
|
/// <exception cref="ArgumentNullException"><paramref name="key"/> must be non-null</exception>
|
|
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));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines if the given <typeparamref name="TValue"/> exists within this <see cref="MultiValueDictionary{TKey,TValue}"/>.
|
|
/// </summary>
|
|
/// <param name="value">A <typeparamref name="TValue"/> to search the <see cref="MultiValueDictionary{TKey,TValue}"/> for</param>
|
|
/// <returns><c>true</c> if the <see cref="MultiValueDictionary{TKey,TValue}"/> contains the <paramref name="value"/>; otherwise <c>false</c></returns>
|
|
public bool ContainsValue(TValue value)
|
|
{
|
|
foreach (InnerCollectionView sublist in dictionary.Values)
|
|
if (sublist.Contains(value))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes every <typeparamref name="TKey"/> and <typeparamref name="TValue"/> from this
|
|
/// <see cref="MultiValueDictionary{TKey,TValue}"/>.
|
|
/// </summary>
|
|
public void Clear()
|
|
{
|
|
dictionary.Clear();
|
|
version++;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Members implemented from IReadOnlyDictionary<TKey, IReadOnlyCollection<TValue>>
|
|
/*======================================================================
|
|
** Members implemented from IReadOnlyDictionary<TKey, IReadOnlyCollection<TValue>>
|
|
======================================================================*/
|
|
|
|
/// <summary>
|
|
/// Determines if the given <typeparamref name="TKey"/> exists within this <see cref="MultiValueDictionary{TKey,TValue}"/> and has
|
|
/// at least one <typeparamref name="TValue"/> associated with it.
|
|
/// </summary>
|
|
/// <param name="key">The <typeparamref name="TKey"/> to search the <see cref="MultiValueDictionary{TKey,TValue}"/> for</param>
|
|
/// <returns><c>true</c> if the <see cref="MultiValueDictionary{TKey,TValue}"/> contains the requested <typeparamref name="TKey"/>;
|
|
/// otherwise <c>false</c>.</returns>
|
|
/// <exception cref="ArgumentNullException"><paramref name="key"/> must be non-null</exception>
|
|
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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets each <typeparamref name="TKey"/> in this <see cref="MultiValueDictionary{TKey,TValue}"/> that
|
|
/// has one or more associated <typeparamref name="TValue"/>.
|
|
/// </summary>
|
|
/// <value>
|
|
/// An <see cref="IEnumerable{TKey}"/> containing each <typeparamref name="TKey"/>
|
|
/// in this <see cref="MultiValueDictionary{TKey,TValue}"/> that has one or more associated
|
|
/// <typeparamref name="TValue"/>.
|
|
/// </value>
|
|
public IEnumerable<TKey> Keys
|
|
{
|
|
get
|
|
{
|
|
return dictionary.Keys;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Attempts to get the <typeparamref name="TValue"/> associated with the given
|
|
/// <typeparamref name="TKey"/> and place it into <paramref name="value"/>.
|
|
/// </summary>
|
|
/// <param name="key">The <typeparamref name="TKey"/> of the element to retrieve</param>
|
|
/// <param name="value">
|
|
/// When this method returns, contains the <typeparamref name="TValue"/> associated with the specified
|
|
/// <typeparamref name="TKey"/> if it is found; otherwise contains the default value of <typeparamref name="TValue"/>.
|
|
/// </param>
|
|
/// <returns>
|
|
/// <c>true</c> if the <see cref="MultiValueDictionary{TKey,TValue}"/> contains an element with the specified
|
|
/// <typeparamref name="TKey"/>; otherwise, <c>false</c>.
|
|
/// </returns>
|
|
/// <exception cref="ArgumentNullException"><paramref name="key"/> must be non-null</exception>
|
|
public bool TryGetValue(TKey key, out IReadOnlyCollection<TValue> value)
|
|
{
|
|
if (key == null)
|
|
throw new ArgumentNullException("key");
|
|
|
|
InnerCollectionView collection;
|
|
var success = dictionary.TryGetValue(key, out collection);
|
|
value = collection;
|
|
return success;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets an enumerable of <see cref="IReadOnlyCollection{TValue}"/> from this <see cref="MultiValueDictionary{TKey,TValue}"/>,
|
|
/// where each <see cref="IReadOnlyCollection{TValue}" /> is the collection of every <typeparamref name="TValue"/> associated
|
|
/// with a <typeparamref name="TKey"/> present in the <see cref="MultiValueDictionary{TKey,TValue}"/>.
|
|
/// </summary>
|
|
/// <value>An IEnumerable of each <see cref="IReadOnlyCollection{TValue}"/> in this
|
|
/// <see cref="MultiValueDictionary{TKey,TValue}"/></value>
|
|
public IEnumerable<IReadOnlyCollection<TValue>> Values
|
|
{
|
|
get
|
|
{
|
|
return dictionary.Values;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get every <typeparamref name="TValue"/> associated with the given <typeparamref name="TKey"/>. If
|
|
/// <paramref name="key"/> is not found in this <see cref="MultiValueDictionary{TKey,TValue}"/>, will
|
|
/// throw a <see cref="KeyNotFoundException"/>.
|
|
/// </summary>
|
|
/// <param name="key">The <typeparamref name="TKey"/> of the elements to retrieve.</param>
|
|
/// <exception cref="ArgumentNullException"><paramref name="key"/> must be non-null</exception>
|
|
/// <exception cref="KeyNotFoundException"><paramref name="key"/> does not have any associated
|
|
/// <typeparamref name="TValue"/>s in this <see cref="MultiValueDictionary{TKey,TValue}"/>.</exception>
|
|
/// <value>
|
|
/// An <see cref="IReadOnlyCollection{TValue}"/> containing every <typeparamref name="TValue"/>
|
|
/// associated with <paramref name="key"/>.
|
|
/// </value>
|
|
/// <remarks>
|
|
/// Note that the <see cref="IReadOnlyCollection{TValue}"/> returned will change alongside any changes
|
|
/// to the <see cref="MultiValueDictionary{TKey,TValue}"/>
|
|
/// </remarks>
|
|
public IReadOnlyCollection<TValue> 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();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the number of <typeparamref name="TKey"/>s with one or more associated <typeparamref name="TValue"/>
|
|
/// in this <see cref="MultiValueDictionary{TKey,TValue}"/>.
|
|
/// </summary>
|
|
/// <value>The number of <typeparamref name="TKey"/>s in this <see cref="MultiValueDictionary{TKey,TValue}"/>.</value>
|
|
public int Count
|
|
{
|
|
get
|
|
{
|
|
return dictionary.Count;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get an Enumerator over the <typeparamref name="TKey"/>-<see cref="IReadOnlyCollection{TValue}"/>
|
|
/// pairs in this <see cref="MultiValueDictionary{TKey,TValue}"/>.
|
|
/// </summary>
|
|
/// <returns>an Enumerator over the <typeparamref name="TKey"/>-<see cref="IReadOnlyCollection{TValue}"/>
|
|
/// pairs in this <see cref="MultiValueDictionary{TKey,TValue}"/>.</returns>
|
|
public IEnumerator<KeyValuePair<TKey, IReadOnlyCollection<TValue>>> GetEnumerator()
|
|
{
|
|
return new Enumerator(this);
|
|
}
|
|
|
|
IEnumerator IEnumerable.GetEnumerator()
|
|
{
|
|
return new Enumerator(this);
|
|
}
|
|
|
|
#endregion
|
|
|
|
/// <summary>
|
|
/// The Enumerator class for a <see cref="MultiValueDictionary{TKey, TValue}"/>
|
|
/// that iterates over <typeparamref name="TKey"/>-<see cref="IReadOnlyCollection{TValue}"/>
|
|
/// pairs.
|
|
/// </summary>
|
|
private class Enumerator :
|
|
IEnumerator<KeyValuePair<TKey, IReadOnlyCollection<TValue>>>
|
|
{
|
|
private readonly MultiValueDictionary<TKey, TValue> multiValueDictionary;
|
|
private readonly int version;
|
|
private KeyValuePair<TKey, IReadOnlyCollection<TValue>> current;
|
|
private Dictionary<TKey, InnerCollectionView>.Enumerator enumerator;
|
|
private enum EnumerationState { BeforeFirst, During, AfterLast };
|
|
private EnumerationState state;
|
|
|
|
/// <summary>
|
|
/// Constructor for the enumerator
|
|
/// </summary>
|
|
/// <param name="multiValueDictionary">A MultiValueDictionary to iterate over</param>
|
|
internal Enumerator(MultiValueDictionary<TKey, TValue> multiValueDictionary)
|
|
{
|
|
this.multiValueDictionary = multiValueDictionary;
|
|
this.version = multiValueDictionary.version;
|
|
this.current = default(KeyValuePair<TKey, IReadOnlyCollection<TValue>>);
|
|
this.enumerator = multiValueDictionary.dictionary.GetEnumerator();
|
|
this.state = EnumerationState.BeforeFirst;
|
|
;
|
|
}
|
|
|
|
public KeyValuePair<TKey, IReadOnlyCollection<TValue>> 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;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Advances the enumerator to the next element of the collection.
|
|
/// </summary>
|
|
/// <returns>
|
|
/// true if the enumerator was successfully advanced to the next element; false if the enumerator has passed the end of the collection.
|
|
/// </returns>
|
|
/// <exception cref="T:System.InvalidOperationException">The collection was modified after the enumerator was created. </exception>
|
|
public bool MoveNext()
|
|
{
|
|
if (version != multiValueDictionary.version)
|
|
{
|
|
throw new InvalidOperationException("Properties.Resources.InvalidOperation_EnumFailedVersion");
|
|
}
|
|
else if (enumerator.MoveNext())
|
|
{
|
|
current = new KeyValuePair<TKey, IReadOnlyCollection<TValue>>(enumerator.Current.Key, enumerator.Current.Value);
|
|
state = EnumerationState.During;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
current = default(KeyValuePair<TKey, IReadOnlyCollection<TValue>>);
|
|
state = EnumerationState.AfterLast;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the enumerator to its initial position, which is before the first element in the collection.
|
|
/// </summary>
|
|
/// <exception cref="T:System.InvalidOperationException">The collection was modified after the enumerator was created. </exception>
|
|
public void Reset()
|
|
{
|
|
if (version != multiValueDictionary.version)
|
|
throw new InvalidOperationException("Properties.Resources.InvalidOperation_EnumFailedVersion");
|
|
enumerator.Dispose();
|
|
enumerator = multiValueDictionary.dictionary.GetEnumerator();
|
|
current = default(KeyValuePair<TKey, IReadOnlyCollection<TValue>>);
|
|
state = EnumerationState.BeforeFirst;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Frees resources associated with this Enumerator
|
|
/// </summary>
|
|
public void Dispose()
|
|
{
|
|
enumerator.Dispose();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// An inner class that functions as a view of an ICollection within a MultiValueDictionary
|
|
/// </summary>
|
|
private class InnerCollectionView :
|
|
ICollection<TValue>,
|
|
IReadOnlyCollection<TValue>
|
|
{
|
|
private readonly TKey key;
|
|
private readonly ICollection<TValue> collection;
|
|
|
|
#region Private Concrete API
|
|
/*======================================================================
|
|
** Private Concrete API
|
|
======================================================================*/
|
|
|
|
public InnerCollectionView(TKey key, ICollection<TValue> 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<TValue> 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<TValue>.Add(TValue item)
|
|
{
|
|
throw new NotSupportedException("Properties.Resources.ReadOnly_Modification");
|
|
}
|
|
|
|
void ICollection<TValue>.Clear()
|
|
{
|
|
throw new NotSupportedException("Properties.Resources.ReadOnly_Modification");
|
|
}
|
|
|
|
bool ICollection<TValue>.Remove(TValue item)
|
|
{
|
|
throw new NotSupportedException("Properties.Resources.ReadOnly_Modification");
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|
|
|
|
public static class MultiValueDictionaryExtensions
|
|
{
|
|
public static MultiValueDictionary<TKey, TValue> ToMultiValueDictionary<TInput, TKey, TValue>(this IEnumerable<TInput> collection, Func<TInput, TKey> keySelector, Func<TInput, TValue> valueSelector)
|
|
{
|
|
var dictionary = new MultiValueDictionary<TKey, TValue>();
|
|
foreach (var item in collection)
|
|
{
|
|
dictionary.Add(keySelector(item), valueSelector(item));
|
|
}
|
|
return dictionary;
|
|
}
|
|
}
|
|
}
|