|  | @@ -34,7 +34,6 @@ using System;
 | 
	
		
			
				|  |  |  using System.Collections;
 | 
	
		
			
				|  |  |  using System.Collections.Generic;
 | 
	
		
			
				|  |  |  using System.IO;
 | 
	
		
			
				|  |  | -using System.Text;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  namespace Google.Protobuf.Collections
 | 
	
		
			
				|  |  |  {
 | 
	
	
		
			
				|  | @@ -227,10 +226,7 @@ namespace Google.Protobuf.Collections
 | 
	
		
			
				|  |  |          /// <param name="item">The item to add.</param>
 | 
	
		
			
				|  |  |          public void Add(T item)
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  | -            if (item == null)
 | 
	
		
			
				|  |  | -            {
 | 
	
		
			
				|  |  | -                throw new ArgumentNullException("item");
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | +            ProtoPreconditions.CheckNotNullUnconstrained(item, nameof(item));
 | 
	
		
			
				|  |  |              EnsureSize(count + 1);
 | 
	
		
			
				|  |  |              array[count++] = item;
 | 
	
		
			
				|  |  |          }
 | 
	
	
		
			
				|  | @@ -285,46 +281,82 @@ namespace Google.Protobuf.Collections
 | 
	
		
			
				|  |  |          /// <summary>
 | 
	
		
			
				|  |  |          /// Gets the number of elements contained in the collection.
 | 
	
		
			
				|  |  |          /// </summary>
 | 
	
		
			
				|  |  | -        public int Count { get { return count; } }
 | 
	
		
			
				|  |  | +        public int Count => count;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          /// <summary>
 | 
	
		
			
				|  |  |          /// Gets a value indicating whether the collection is read-only.
 | 
	
		
			
				|  |  |          /// </summary>
 | 
	
		
			
				|  |  | -        public bool IsReadOnly { get { return false; } }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        // TODO: Remove this overload and just handle it in the one below, at execution time?
 | 
	
		
			
				|  |  | +        public bool IsReadOnly => false;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          /// <summary>
 | 
	
		
			
				|  |  |          /// Adds all of the specified values into this collection.
 | 
	
		
			
				|  |  |          /// </summary>
 | 
	
		
			
				|  |  |          /// <param name="values">The values to add to this collection.</param>
 | 
	
		
			
				|  |  | -        public void Add(RepeatedField<T> values)
 | 
	
		
			
				|  |  | +        public void AddRange(IEnumerable<T> values)
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  | -            if (values == null)
 | 
	
		
			
				|  |  | +            ProtoPreconditions.CheckNotNull(values, nameof(values));
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            // Optimization 1: If the collection we're adding is already a RepeatedField<T>,
 | 
	
		
			
				|  |  | +            // we know the values are valid.
 | 
	
		
			
				|  |  | +            var otherRepeatedField = values as RepeatedField<T>;
 | 
	
		
			
				|  |  | +            if (otherRepeatedField != null)
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  | -                throw new ArgumentNullException("values");
 | 
	
		
			
				|  |  | +                EnsureSize(count + otherRepeatedField.count);
 | 
	
		
			
				|  |  | +                Array.Copy(otherRepeatedField.array, 0, array, count, otherRepeatedField.count);
 | 
	
		
			
				|  |  | +                count += otherRepeatedField.count;
 | 
	
		
			
				|  |  | +                return;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            // Optimization 2: The collection is an ICollection, so we can expand
 | 
	
		
			
				|  |  | +            // just once and ask the collection to copy itself into the array.
 | 
	
		
			
				|  |  | +            var collection = values as ICollection;
 | 
	
		
			
				|  |  | +            if (collection != null)
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                var extraCount = collection.Count;
 | 
	
		
			
				|  |  | +                // For reference types and nullable value types, we need to check that there are no nulls
 | 
	
		
			
				|  |  | +                // present. (This isn't a thread-safe approach, but we don't advertise this is thread-safe.)
 | 
	
		
			
				|  |  | +                // We expect the JITter to optimize this test to true/false, so it's effectively conditional
 | 
	
		
			
				|  |  | +                // specialization.
 | 
	
		
			
				|  |  | +                if (default(T) == null)
 | 
	
		
			
				|  |  | +                {
 | 
	
		
			
				|  |  | +                    // TODO: Measure whether iterating once to check and then letting the collection copy
 | 
	
		
			
				|  |  | +                    // itself is faster or slower than iterating and adding as we go. For large
 | 
	
		
			
				|  |  | +                    // collections this will not be great in terms of cache usage... but the optimized
 | 
	
		
			
				|  |  | +                    // copy may be significantly faster than doing it one at a time.
 | 
	
		
			
				|  |  | +                    foreach (var item in collection)
 | 
	
		
			
				|  |  | +                    {
 | 
	
		
			
				|  |  | +                        if (item == null)
 | 
	
		
			
				|  |  | +                        {
 | 
	
		
			
				|  |  | +                            throw new ArgumentException("Sequence contained null element", nameof(values));
 | 
	
		
			
				|  |  | +                        }
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +                EnsureSize(count + extraCount);
 | 
	
		
			
				|  |  | +                collection.CopyTo(array, count);
 | 
	
		
			
				|  |  | +                count += extraCount;
 | 
	
		
			
				|  |  | +                return;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            // We *could* check for ICollection<T> as well, but very very few collections implement
 | 
	
		
			
				|  |  | +            // ICollection<T> but not ICollection. (HashSet<T> does, for one...)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            // Fall back to a slower path of adding items one at a time.
 | 
	
		
			
				|  |  | +            foreach (T item in values)
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                Add(item);
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  | -            EnsureSize(count + values.count);
 | 
	
		
			
				|  |  | -            // We know that all the values will be valid, because it's a RepeatedField.
 | 
	
		
			
				|  |  | -            Array.Copy(values.array, 0, array, count, values.count);
 | 
	
		
			
				|  |  | -            count += values.count;
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          /// <summary>
 | 
	
		
			
				|  |  | -        /// Adds all of the specified values into this collection.
 | 
	
		
			
				|  |  | +        /// Adds all of the specified values into this collection. This method is present to
 | 
	
		
			
				|  |  | +        /// allow repeated fields to be constructed from queries within collection initializers.
 | 
	
		
			
				|  |  | +        /// Within non-collection-initializer code, consider using the equivalent <see cref="AddRange"/>
 | 
	
		
			
				|  |  | +        /// method instead for clarity.
 | 
	
		
			
				|  |  |          /// </summary>
 | 
	
		
			
				|  |  |          /// <param name="values">The values to add to this collection.</param>
 | 
	
		
			
				|  |  |          public void Add(IEnumerable<T> values)
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  | -            if (values == null)
 | 
	
		
			
				|  |  | -            {
 | 
	
		
			
				|  |  | -                throw new ArgumentNullException("values");
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -            // TODO: Check for ICollection and get the Count, to optimize?
 | 
	
		
			
				|  |  | -            foreach (T item in values)
 | 
	
		
			
				|  |  | -            {
 | 
	
		
			
				|  |  | -                Add(item);
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | +            AddRange(values);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          /// <summary>
 | 
	
	
		
			
				|  | @@ -418,10 +450,7 @@ namespace Google.Protobuf.Collections
 | 
	
		
			
				|  |  |          /// <returns>The zero-based index of the item, or -1 if it is not found.</returns>
 | 
	
		
			
				|  |  |          public int IndexOf(T item)
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  | -            if (item == null)
 | 
	
		
			
				|  |  | -            {
 | 
	
		
			
				|  |  | -                throw new ArgumentNullException("item");
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | +            ProtoPreconditions.CheckNotNullUnconstrained(item, nameof(item));
 | 
	
		
			
				|  |  |              EqualityComparer<T> comparer = EqualityComparer<T>.Default;
 | 
	
		
			
				|  |  |              for (int i = 0; i < count; i++)
 | 
	
		
			
				|  |  |              {
 | 
	
	
		
			
				|  | @@ -440,13 +469,10 @@ namespace Google.Protobuf.Collections
 | 
	
		
			
				|  |  |          /// <param name="item">The item to insert.</param>
 | 
	
		
			
				|  |  |          public void Insert(int index, T item)
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  | -            if (item == null)
 | 
	
		
			
				|  |  | -            {
 | 
	
		
			
				|  |  | -                throw new ArgumentNullException("item");
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | +            ProtoPreconditions.CheckNotNullUnconstrained(item, nameof(item));
 | 
	
		
			
				|  |  |              if (index < 0 || index > count)
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  | -                throw new ArgumentOutOfRangeException("index");
 | 
	
		
			
				|  |  | +                throw new ArgumentOutOfRangeException(nameof(index));
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |              EnsureSize(count + 1);
 | 
	
		
			
				|  |  |              Array.Copy(array, index, array, index + 1, count - index);
 | 
	
	
		
			
				|  | @@ -462,7 +488,7 @@ namespace Google.Protobuf.Collections
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  |              if (index < 0 || index >= count)
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  | -                throw new ArgumentOutOfRangeException("index");
 | 
	
		
			
				|  |  | +                throw new ArgumentOutOfRangeException(nameof(index));
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |              Array.Copy(array, index + 1, array, index, count - index - 1);
 | 
	
		
			
				|  |  |              count--;
 | 
	
	
		
			
				|  | @@ -494,7 +520,7 @@ namespace Google.Protobuf.Collections
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  |                  if (index < 0 || index >= count)
 | 
	
		
			
				|  |  |                  {
 | 
	
		
			
				|  |  | -                    throw new ArgumentOutOfRangeException("index");
 | 
	
		
			
				|  |  | +                    throw new ArgumentOutOfRangeException(nameof(index));
 | 
	
		
			
				|  |  |                  }
 | 
	
		
			
				|  |  |                  return array[index];
 | 
	
		
			
				|  |  |              }
 | 
	
	
		
			
				|  | @@ -502,27 +528,24 @@ namespace Google.Protobuf.Collections
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  |                  if (index < 0 || index >= count)
 | 
	
		
			
				|  |  |                  {
 | 
	
		
			
				|  |  | -                    throw new ArgumentOutOfRangeException("index");
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | -                if (value == null)
 | 
	
		
			
				|  |  | -                {
 | 
	
		
			
				|  |  | -                    throw new ArgumentNullException("value");
 | 
	
		
			
				|  |  | +                    throw new ArgumentOutOfRangeException(nameof(index));
 | 
	
		
			
				|  |  |                  }
 | 
	
		
			
				|  |  | +                ProtoPreconditions.CheckNotNullUnconstrained(value, nameof(value));
 | 
	
		
			
				|  |  |                  array[index] = value;
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          #region Explicit interface implementation for IList and ICollection.
 | 
	
		
			
				|  |  | -        bool IList.IsFixedSize { get { return false; } }
 | 
	
		
			
				|  |  | +        bool IList.IsFixedSize => false;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          void ICollection.CopyTo(Array array, int index)
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  |              Array.Copy(this.array, 0, array, index, count);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        bool ICollection.IsSynchronized { get { return false; } }
 | 
	
		
			
				|  |  | +        bool ICollection.IsSynchronized => false;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        object ICollection.SyncRoot { get { return this; } }
 | 
	
		
			
				|  |  | +        object ICollection.SyncRoot => this;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          object IList.this[int index]
 | 
	
		
			
				|  |  |          {
 |