-
Notifications
You must be signed in to change notification settings - Fork 4.8k
Add ExtractToImmutable #180
Changes from all commits
61f778d
101e03d
0be9fb3
f51ba88
e034c93
c743cfb
dd2b6ea
d4f998e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -22,7 +22,7 @@ public sealed class Builder : IList<T>, IReadOnlyList<T> | |
| /// <summary> | ||
| /// The backing array for the builder. | ||
| /// </summary> | ||
| private RefAsValueType<T>[] _elements; | ||
| private T[] _elements; | ||
|
|
||
| /// <summary> | ||
| /// The number of initialized elements in the array. | ||
|
|
@@ -36,8 +36,8 @@ public sealed class Builder : IList<T>, IReadOnlyList<T> | |
| internal Builder(int capacity) | ||
| { | ||
| Requires.Range(capacity >= 0, "capacity"); | ||
| _elements = new RefAsValueType<T>[capacity]; | ||
| this.Count = 0; | ||
| _elements = new T[capacity]; | ||
| _count = 0; | ||
| } | ||
|
|
||
| /// <summary> | ||
|
|
@@ -49,7 +49,41 @@ internal Builder() | |
| } | ||
|
|
||
| /// <summary> | ||
| /// Gets or sets the length of the array. | ||
| /// Get and sets the length of the internal array. When set the internal array is | ||
| /// is reallocated to the given capacity if it is not already the specified length. | ||
| /// </summary> | ||
| public int Capacity | ||
| { | ||
| get { return _elements.Length; } | ||
| set | ||
| { | ||
| if (value < _count) | ||
| { | ||
| throw new ArgumentException(Strings.CapacityMustBeGreaterThanOrEqualToCount, paramName: "value"); | ||
| } | ||
|
|
||
| if (value != _elements.Length) | ||
| { | ||
| if (value > 0) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
if (value > 0)
{
Array.Resize(ref _elements, value);
} |
||
| { | ||
| var temp = new T[value]; | ||
| if (_count > 0) | ||
| { | ||
| Array.Copy(_elements, 0, temp, 0, _count); | ||
| } | ||
|
|
||
| _elements = temp; | ||
| } | ||
| else | ||
| { | ||
| _elements = ImmutableArray<T>.Empty.array; | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Gets or sets the length of the builder. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// If the value is decreased, the array contents are truncated. | ||
|
|
@@ -81,7 +115,7 @@ public int Count | |
| { | ||
| for (int i = value; i < this.Count; i++) | ||
| { | ||
| _elements[i].Value = default(T); | ||
| _elements[i] = default(T); | ||
| } | ||
| } | ||
| } | ||
|
|
@@ -111,7 +145,7 @@ public T this[int index] | |
| throw new IndexOutOfRangeException(); | ||
| } | ||
|
|
||
| return _elements[index].Value; | ||
| return _elements[index]; | ||
| } | ||
|
|
||
| set | ||
|
|
@@ -121,7 +155,7 @@ public T this[int index] | |
| throw new IndexOutOfRangeException(); | ||
| } | ||
|
|
||
| _elements[index].Value = value; | ||
| _elements[index] = value; | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -141,14 +175,33 @@ bool ICollection<T>.IsReadOnly | |
| /// <returns>An immutable array.</returns> | ||
| public ImmutableArray<T> ToImmutable() | ||
| { | ||
| if (this.Count == 0) | ||
| if (Count == 0) | ||
| { | ||
| return Empty; | ||
| } | ||
|
|
||
| return new ImmutableArray<T>(this.ToArray()); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Extracts the internal array as an <see cref="ImmutableArray{T}"/> and replaces it | ||
| /// with a zero length array. | ||
| /// </summary> | ||
| /// <exception cref="InvalidOperationException">When <see cref="ImmutableArray{T}.Builder.Count"/> doesn't | ||
| /// equal <see cref="ImmutableArray{T}.Builder.Capacity"/>.</exception> | ||
| public ImmutableArray<T> MoveToImmutable() | ||
| { | ||
| if (Capacity != Count) | ||
| { | ||
| throw new InvalidOperationException(Strings.CapacityMustEqualCountOnMove); | ||
| } | ||
|
|
||
| T[] temp = _elements; | ||
| _elements = ImmutableArray<T>.Empty.array; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you please explicitly type temp here to T[]? |
||
| _count = 0; | ||
| return new ImmutableArray<T>(temp); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Removes all items from the <see cref="T:System.Collections.Generic.ICollection`1" />. | ||
| /// </summary> | ||
|
|
@@ -173,7 +226,7 @@ public void Insert(int index, T item) | |
| } | ||
|
|
||
| _count++; | ||
| _elements[index].Value = item; | ||
| _elements[index] = item; | ||
| } | ||
|
|
||
| /// <summary> | ||
|
|
@@ -183,7 +236,7 @@ public void Insert(int index, T item) | |
| public void Add(T item) | ||
| { | ||
| this.EnsureCapacity(this.Count + 1); | ||
| _elements[_count++].Value = item; | ||
| _elements[_count++] = item; | ||
| } | ||
|
|
||
| /// <summary> | ||
|
|
@@ -220,7 +273,7 @@ public void AddRange(params T[] items) | |
| var nodes = _elements; | ||
| for (int i = 0; i < items.Length; i++) | ||
| { | ||
| nodes[offset + i].Value = items[i]; | ||
| nodes[offset + i] = items[i]; | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -238,7 +291,7 @@ public void AddRange<TDerived>(TDerived[] items) where TDerived : T | |
| var nodes = _elements; | ||
| for (int i = 0; i < items.Length; i++) | ||
| { | ||
| nodes[offset + i].Value = items[i]; | ||
| nodes[offset + i] = items[i]; | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -258,7 +311,7 @@ public void AddRange(T[] items, int length) | |
| var nodes = _elements; | ||
| for (int i = 0; i < length; i++) | ||
| { | ||
| nodes[offset + i].Value = items[i]; | ||
| nodes[offset + i] = items[i]; | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -372,7 +425,7 @@ public T[] ToArray() | |
| var elements = _elements; | ||
| for (int i = 0; i < tmp.Length; i++) | ||
| { | ||
| tmp[i] = elements[i].Value; | ||
| tmp[i] = elements[i]; | ||
| } | ||
|
|
||
| return tmp; | ||
|
|
@@ -387,18 +440,14 @@ public void CopyTo(T[] array, int index) | |
| { | ||
| Requires.NotNull(array, "array"); | ||
| Requires.Range(index >= 0 && index + this.Count <= array.Length, "start"); | ||
|
|
||
| foreach (var item in this) | ||
| { | ||
| array[index++] = item; | ||
| } | ||
| Array.Copy(_elements, 0, array, index, this.Count); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Resizes the array to accommodate the specified capacity requirement. | ||
| /// </summary> | ||
| /// <param name="capacity">The required capacity.</param> | ||
| public void EnsureCapacity(int capacity) | ||
| private void EnsureCapacity(int capacity) | ||
| { | ||
| if (_elements.Length < capacity) | ||
| { | ||
|
|
@@ -468,13 +517,13 @@ public int IndexOf(T item, int startIndex, int count, IEqualityComparer<T> equal | |
|
|
||
| if (equalityComparer == EqualityComparer<T>.Default) | ||
| { | ||
| return Array.IndexOf(_elements, new RefAsValueType<T>(item), startIndex, count); | ||
| return Array.IndexOf(_elements, item, startIndex, count); | ||
| } | ||
| else | ||
| { | ||
| for (int i = startIndex; i < startIndex + count; i++) | ||
| { | ||
| if (equalityComparer.Equals(_elements[i].Value, item)) | ||
| if (equalityComparer.Equals(_elements[i], item)) | ||
| { | ||
| return i; | ||
| } | ||
|
|
@@ -555,13 +604,13 @@ public int LastIndexOf(T item, int startIndex, int count, IEqualityComparer<T> e | |
|
|
||
| if (equalityComparer == EqualityComparer<T>.Default) | ||
| { | ||
| return Array.LastIndexOf(_elements, new RefAsValueType<T>(item), startIndex, count); | ||
| return Array.LastIndexOf(_elements, item, startIndex, count); | ||
| } | ||
| else | ||
| { | ||
| for (int i = startIndex; i >= startIndex - count + 1; i--) | ||
| { | ||
| if (equalityComparer.Equals(item, _elements[i].Value)) | ||
| if (equalityComparer.Equals(item, _elements[i])) | ||
| { | ||
| return i; | ||
| } | ||
|
|
@@ -576,13 +625,7 @@ public int LastIndexOf(T item, int startIndex, int count, IEqualityComparer<T> e | |
| /// </summary> | ||
| public void ReverseContents() | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would just call it reverse to be consistent with List. |
||
| { | ||
| int end = this.Count - 1; | ||
| for (int i = 0, j = end; i < j; i++, j--) | ||
| { | ||
| var tmp = _elements[i].Value; | ||
| _elements[i] = _elements[j]; | ||
| _elements[j].Value = tmp; | ||
| } | ||
| Array.Reverse(_elements, 0, _count); | ||
| } | ||
|
|
||
| /// <summary> | ||
|
|
@@ -592,7 +635,7 @@ public void Sort() | |
| { | ||
| if (Count > 1) | ||
| { | ||
| Array.Sort(_elements, 0, this.Count, Comparer.Default); | ||
| Array.Sort(_elements, 0, this.Count, Comparer<T>.Default); | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -604,7 +647,7 @@ public void Sort(IComparer<T> comparer) | |
| { | ||
| if (Count > 1) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IIRC, these Count > 1 checks were previously added to avoid allocating the Comparer wrapper. Now that the wrapper isn't needed, I wonder if the checks are still beneficial. |
||
| { | ||
| Array.Sort(_elements, 0, this.Count, Comparer.Create(comparer)); | ||
| Array.Sort(_elements, 0, _count, comparer); | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -623,7 +666,7 @@ public void Sort(int index, int count, IComparer<T> comparer) | |
|
|
||
| if (count > 1) | ||
| { | ||
| Array.Sort(_elements, index, count, Comparer.Create(comparer)); | ||
| Array.Sort(_elements, index, count, comparer); | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -663,7 +706,7 @@ IEnumerator IEnumerable.GetEnumerator() | |
| /// <typeparam name="TDerived">The type of source elements.</typeparam> | ||
| /// <param name="items">The source array.</param> | ||
| /// <param name="length">The number of elements to add to this array.</param> | ||
| private void AddRange<TDerived>(RefAsValueType<TDerived>[] items, int length) where TDerived : T | ||
| private void AddRange<TDerived>(TDerived[] items, int length) where TDerived : T | ||
| { | ||
| this.EnsureCapacity(this.Count + length); | ||
|
|
||
|
|
@@ -673,35 +716,7 @@ private void AddRange<TDerived>(RefAsValueType<TDerived>[] items, int length) wh | |
| var nodes = _elements; | ||
| for (int i = 0; i < length; i++) | ||
| { | ||
| nodes[offset + i].Value = items[i].Value; | ||
| } | ||
| } | ||
|
|
||
| private sealed class Comparer : IComparer<RefAsValueType<T>> | ||
| { | ||
| private readonly IComparer<T> _comparer; | ||
|
|
||
| public static readonly Comparer Default = new Comparer(Comparer<T>.Default); | ||
|
|
||
| public static Comparer Create(IComparer<T> comparer) | ||
| { | ||
| if (comparer == null || comparer == Comparer<T>.Default) | ||
| { | ||
| return Default; | ||
| } | ||
|
|
||
| return new Comparer(comparer); | ||
| } | ||
|
|
||
| private Comparer(IComparer<T> comparer) | ||
| { | ||
| Requires.NotNull(comparer, "comparer"); // use Comparer.Default instead of passing null | ||
| _comparer = comparer; | ||
| } | ||
|
|
||
| public int Compare(RefAsValueType<T> x, RefAsValueType<T> y) | ||
| { | ||
| return _comparer.Compare(x.Value, y.Value); | ||
| nodes[offset + i] = items[i]; | ||
| } | ||
| } | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not just use the following setter?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the case of
value == 0this would add an extra allocation.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually it wouldn't. I missed this on my first read too. Due to the argument validation, the only way
value==0is allowed is when_elements.Length == 0. When that occursArray.Resizeis a NOP because the current length and requested length are the same.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sharwell true.
This particular implementation was basically taken from
List<T>::set_Capacity. Given it's high use within .Net I error'd in just taking that implementation over devising my own.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The implementation for
List<T>.Capacitywas taken fromArrayList.Capacityduring the development of .NET 2.0. At the time that property was added, it's unlikely that theArray.Resize<T>method existed, hence why it was not used there.Considering how much simpler my proposed implementation is, I recommend at least giving it full consideration since it would improve the overall size and readability of the code with (from what I've seen so far) no downside.