diff options
Diffstat (limited to 'csharp/src/Google.Protobuf/Collections/MapField.cs')
-rw-r--r-- | csharp/src/Google.Protobuf/Collections/MapField.cs | 82 |
1 files changed, 47 insertions, 35 deletions
diff --git a/csharp/src/Google.Protobuf/Collections/MapField.cs b/csharp/src/Google.Protobuf/Collections/MapField.cs index 90a5ff1a..dbbcc148 100644 --- a/csharp/src/Google.Protobuf/Collections/MapField.cs +++ b/csharp/src/Google.Protobuf/Collections/MapField.cs @@ -30,13 +30,13 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #endregion +using Google.Protobuf.Compatibility; using Google.Protobuf.Reflection; using System; using System.Collections; using System.Collections.Generic; +using System.IO; using System.Linq; -using System.Text; -using Google.Protobuf.Compatibility; namespace Google.Protobuf.Collections { @@ -47,9 +47,6 @@ namespace Google.Protobuf.Collections /// <typeparam name="TValue">Value type in the map. Must be a type supported by Protocol Buffers.</typeparam> /// <remarks> /// <para> - /// This implementation preserves insertion order for simplicity of testing - /// code using maps fields. Overwriting an existing entry does not change the - /// position of that entry within the map. Equality is not order-sensitive. /// For string keys, the equality comparison is provided by <see cref="StringComparer.Ordinal" />. /// </para> /// <para> @@ -64,12 +61,22 @@ namespace Google.Protobuf.Collections /// supported by Protocol Buffers (e.g. using a key type of <code>byte</code>) but nor does it guarantee /// that all operations will work in such cases. /// </para> + /// <para> + /// The order in which entries are returned when iterating over this object is undefined, and may change + /// in future versions. + /// </para> /// </remarks> public sealed class MapField<TKey, TValue> : IDeepCloneable<MapField<TKey, TValue>>, IDictionary<TKey, TValue>, IEquatable<MapField<TKey, TValue>>, IDictionary +#if !NET35 + , IReadOnlyDictionary<TKey, TValue> +#endif { + private static readonly EqualityComparer<TValue> ValueEqualityComparer = ProtobufEqualityComparers.GetEqualityComparer<TValue>(); + private static readonly EqualityComparer<TKey> KeyEqualityComparer = ProtobufEqualityComparers.GetEqualityComparer<TKey>(); + // TODO: Don't create the map/list until we have an entry. (Assume many maps will be empty.) private readonly Dictionary<TKey, LinkedListNode<KeyValuePair<TKey, TValue>>> map = - new Dictionary<TKey, LinkedListNode<KeyValuePair<TKey, TValue>>>(); + new Dictionary<TKey, LinkedListNode<KeyValuePair<TKey, TValue>>>(KeyEqualityComparer); private readonly LinkedList<KeyValuePair<TKey, TValue>> list = new LinkedList<KeyValuePair<TKey, TValue>>(); /// <summary> @@ -111,7 +118,7 @@ namespace Google.Protobuf.Collections // Validation of arguments happens in ContainsKey and the indexer if (ContainsKey(key)) { - throw new ArgumentException("Key already exists in map", "key"); + throw new ArgumentException("Key already exists in map", nameof(key)); } this[key] = value; } @@ -123,15 +130,12 @@ namespace Google.Protobuf.Collections /// <returns><c>true</c> if the map contains the given key; <c>false</c> otherwise.</returns> public bool ContainsKey(TKey key) { - ProtoPreconditions.CheckNotNullUnconstrained(key, "key"); + ProtoPreconditions.CheckNotNullUnconstrained(key, nameof(key)); return map.ContainsKey(key); } - private bool ContainsValue(TValue value) - { - var comparer = EqualityComparer<TValue>.Default; - return list.Any(pair => comparer.Equals(pair.Value, value)); - } + private bool ContainsValue(TValue value) => + list.Any(pair => ValueEqualityComparer.Equals(pair.Value, value)); /// <summary> /// Removes the entry identified by the given key from the map. @@ -140,7 +144,7 @@ namespace Google.Protobuf.Collections /// <returns><c>true</c> if the map contained the given key before the entry was removed; <c>false</c> otherwise.</returns> public bool Remove(TKey key) { - ProtoPreconditions.CheckNotNullUnconstrained(key, "key"); + ProtoPreconditions.CheckNotNullUnconstrained(key, nameof(key)); LinkedListNode<KeyValuePair<TKey, TValue>> node; if (map.TryGetValue(key, out node)) { @@ -188,7 +192,7 @@ namespace Google.Protobuf.Collections { get { - ProtoPreconditions.CheckNotNullUnconstrained(key, "key"); + ProtoPreconditions.CheckNotNullUnconstrained(key, nameof(key)); TValue value; if (TryGetValue(key, out value)) { @@ -198,11 +202,11 @@ namespace Google.Protobuf.Collections } set { - ProtoPreconditions.CheckNotNullUnconstrained(key, "key"); + ProtoPreconditions.CheckNotNullUnconstrained(key, nameof(key)); // value == null check here is redundant, but avoids boxing. if (value == null) { - ProtoPreconditions.CheckNotNullUnconstrained(value, "value"); + ProtoPreconditions.CheckNotNullUnconstrained(value, nameof(value)); } LinkedListNode<KeyValuePair<TKey, TValue>> node; var pair = new KeyValuePair<TKey, TValue>(key, value); @@ -234,7 +238,7 @@ namespace Google.Protobuf.Collections /// <param name="entries">The entries to add to the map.</param> public void Add(IDictionary<TKey, TValue> entries) { - ProtoPreconditions.CheckNotNull(entries, "entries"); + ProtoPreconditions.CheckNotNull(entries, nameof(entries)); foreach (var pair in entries) { Add(pair.Key, pair.Value); @@ -289,8 +293,7 @@ namespace Google.Protobuf.Collections bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item) { TValue value; - return TryGetValue(item.Key, out value) - && EqualityComparer<TValue>.Default.Equals(item.Value, value); + return TryGetValue(item.Key, out value) && ValueEqualityComparer.Equals(item.Value, value); } /// <summary> @@ -313,7 +316,7 @@ namespace Google.Protobuf.Collections { if (item.Key == null) { - throw new ArgumentException("Key is null", "item"); + throw new ArgumentException("Key is null", nameof(item)); } LinkedListNode<KeyValuePair<TKey, TValue>> node; if (map.TryGetValue(item.Key, out node) && @@ -359,11 +362,12 @@ namespace Google.Protobuf.Collections /// </returns> public override int GetHashCode() { - var valueComparer = EqualityComparer<TValue>.Default; + var keyComparer = KeyEqualityComparer; + var valueComparer = ValueEqualityComparer; int hash = 0; foreach (var pair in list) { - hash ^= pair.Key.GetHashCode() * 31 + valueComparer.GetHashCode(pair.Value); + hash ^= keyComparer.GetHashCode(pair.Key) * 31 + valueComparer.GetHashCode(pair.Value); } return hash; } @@ -390,7 +394,7 @@ namespace Google.Protobuf.Collections { return false; } - var valueComparer = EqualityComparer<TValue>.Default; + var valueComparer = ValueEqualityComparer; foreach (var pair in this) { TValue value; @@ -474,9 +478,9 @@ namespace Google.Protobuf.Collections /// </summary> public override string ToString() { - var builder = new StringBuilder(); - JsonFormatter.Default.WriteDictionary(builder, this); - return builder.ToString(); + var writer = new StringWriter(); + JsonFormatter.Default.WriteDictionary(writer, this); + return writer.ToString(); } #region IDictionary explicit interface implementation @@ -501,7 +505,7 @@ namespace Google.Protobuf.Collections void IDictionary.Remove(object key) { - ProtoPreconditions.CheckNotNull(key, "key"); + ProtoPreconditions.CheckNotNull(key, nameof(key)); if (!(key is TKey)) { return; @@ -530,7 +534,7 @@ namespace Google.Protobuf.Collections { get { - ProtoPreconditions.CheckNotNull(key, "key"); + ProtoPreconditions.CheckNotNull(key, nameof(key)); if (!(key is TKey)) { return null; @@ -547,6 +551,14 @@ namespace Google.Protobuf.Collections } #endregion + #region IReadOnlyDictionary explicit interface implementation +#if !NET35 + IEnumerable<TKey> IReadOnlyDictionary<TKey, TValue>.Keys => Keys; + + IEnumerable<TValue> IReadOnlyDictionary<TKey, TValue>.Values => Values; +#endif + #endregion + private class DictionaryEnumerator : IDictionaryEnumerator { private readonly IEnumerator<KeyValuePair<TKey, TValue>> enumerator; @@ -712,11 +724,11 @@ namespace Google.Protobuf.Collections { if (arrayIndex < 0) { - throw new ArgumentOutOfRangeException("arrayIndex"); + throw new ArgumentOutOfRangeException(nameof(arrayIndex)); } - if (arrayIndex + Count >= array.Length) + if (arrayIndex + Count > array.Length) { - throw new ArgumentException("Not enough space in the array", "array"); + throw new ArgumentException("Not enough space in the array", nameof(array)); } foreach (var item in this) { @@ -743,11 +755,11 @@ namespace Google.Protobuf.Collections { if (index < 0) { - throw new ArgumentOutOfRangeException("index"); + throw new ArgumentOutOfRangeException(nameof(index)); } - if (index + Count >= array.Length) + if (index + Count > array.Length) { - throw new ArgumentException("Not enough space in the array", "array"); + throw new ArgumentException("Not enough space in the array", nameof(array)); } foreach (var item in this) { |