diff --git a/src/Microsoft.Framework.Primitives/StringValues.cs b/src/Microsoft.Framework.Primitives/StringValues.cs
index a9904aac5c..79a8e6d006 100644
--- a/src/Microsoft.Framework.Primitives/StringValues.cs
+++ b/src/Microsoft.Framework.Primitives/StringValues.cs
@@ -10,7 +10,7 @@ namespace Microsoft.Framework.Primitives
///
/// Represents zero/null, one, or many strings in an efficient way.
///
- public struct StringValues : IList
+ public struct StringValues : IList, IReadOnlyList
{
private static readonly string[] EmptyArray = new string[0];
public static readonly StringValues Empty = new StringValues(EmptyArray);
@@ -63,15 +63,15 @@ namespace Microsoft.Framework.Primitives
set { throw new NotSupportedException(); }
}
- public string this[int key]
+ public string this[int index]
{
get
{
if (_values != null)
{
- return _values[key]; // may throw
+ return _values[index]; // may throw
}
- if (key == 0 && _value != null)
+ if (index == 0 && _value != null)
{
return _value;
}
@@ -114,28 +114,67 @@ namespace Microsoft.Framework.Primitives
int IList.IndexOf(string item)
{
- var index = 0;
- foreach (var value in this)
+ return IndexOf(item);
+ }
+
+ private int IndexOf(string item)
+ {
+ if (_values != null)
{
- if (string.Equals(value, item, StringComparison.Ordinal))
+ var values = _values;
+ for (int i = 0; i < values.Length; i++)
{
- return index;
+ if (string.Equals(values[i], item, StringComparison.Ordinal))
+ {
+ return i;
+ }
}
- index += 1;
+ return -1;
}
+
+ if (_value != null)
+ {
+ return string.Equals(_value, item, StringComparison.Ordinal) ? 0 : -1;
+ }
+
return -1;
}
bool ICollection.Contains(string item)
{
- return ((IList)this).IndexOf(item) >= 0;
+ return IndexOf(item) >= 0;
}
void ICollection.CopyTo(string[] array, int arrayIndex)
{
- for(int i = 0; i < Count; i++)
+ CopyTo(array, arrayIndex);
+ }
+
+ private void CopyTo(string[] array, int arrayIndex)
+ {
+ if (_values != null)
{
- array[arrayIndex + i] = this[i];
+ Array.Copy(_values, 0, array, arrayIndex, _values.Length);
+ return;
+ }
+
+ if (_value != null)
+ {
+ if (array == null)
+ {
+ throw new ArgumentNullException(nameof(array));
+ }
+ if (arrayIndex < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(arrayIndex));
+ }
+ if (array.Length - arrayIndex < 1)
+ {
+ throw new ArgumentException(
+ $"'{nameof(array)}' is not long enough to copy all the items in the collection. Check '{nameof(arrayIndex)}' and '{nameof(array)}' length.");
+ }
+
+ array[arrayIndex] = _value;
}
}
@@ -164,28 +203,19 @@ namespace Microsoft.Framework.Primitives
throw new NotSupportedException();
}
- IEnumerator IEnumerable.GetEnumerator()
+ public Enumerator GetEnumerator()
{
- return ((IEnumerable)this).GetEnumerator();
+ return new Enumerator(this);
}
IEnumerator IEnumerable.GetEnumerator()
{
- if (Count == 0)
- {
- yield break;
- }
- if (_values == null)
- {
- yield return _value;
- }
- else
- {
- for (int i = 0; i < _values.Length; i++)
- {
- yield return _values[i];
- }
- }
+ return GetEnumerator();
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
}
public static bool IsNullOrEmpty(StringValues value)
@@ -218,16 +248,65 @@ namespace Microsoft.Framework.Primitives
}
var combined = new string[count1 + count2];
- var index = 0;
- foreach (var value in values1)
- {
- combined[index++] = value;
- }
- foreach (var value in values2)
- {
- combined[index++] = value;
- }
+ values1.CopyTo(combined, 0);
+ values2.CopyTo(combined, count1);
return new StringValues(combined);
}
+
+ public struct Enumerator : IEnumerator
+ {
+ private readonly StringValues _values;
+ private string _current;
+ private int _index;
+
+ public Enumerator(StringValues values)
+ {
+ _values = values;
+ _current = null;
+ _index = 0;
+ }
+
+ public bool MoveNext()
+ {
+ var values = _values._values;
+ if (values != null)
+ {
+ if (_index < values.Length)
+ {
+ _current = values[_index];
+ _index++;
+ return true;
+ }
+
+ _current = null;
+ return false;
+ }
+
+ var value = _values._value;
+ if (value != null && _index == 0)
+ {
+ _current = value;
+ _index = -1; // sentinel value
+ return true;
+ }
+
+ _current = null;
+ return false;
+ }
+
+ public string Current => _current;
+
+ object IEnumerator.Current => _current;
+
+ void IEnumerator.Reset()
+ {
+ _current = null;
+ _index = 0;
+ }
+
+ void IDisposable.Dispose()
+ {
+ }
+ }
}
}
diff --git a/test/Microsoft.Framework.Primitives.Tests/StringValuesTests.cs b/test/Microsoft.Framework.Primitives.Tests/StringValuesTests.cs
index bdf8e7bca3..184fa3f7da 100644
--- a/test/Microsoft.Framework.Primitives.Tests/StringValuesTests.cs
+++ b/test/Microsoft.Framework.Primitives.Tests/StringValuesTests.cs
@@ -171,6 +171,14 @@ namespace Microsoft.Framework.Primitives
[MemberData(nameof(EmptyStringValues))]
public void DefaultNullOrEmpty_Enumerator(StringValues stringValues)
{
+ var e = stringValues.GetEnumerator();
+ Assert.Null(e.Current);
+ Assert.False(e.MoveNext());
+ Assert.Null(e.Current);
+ Assert.False(e.MoveNext());
+ Assert.False(e.MoveNext());
+ Assert.False(e.MoveNext());
+
var e1 = ((IEnumerable)stringValues).GetEnumerator();
Assert.Null(e1.Current);
Assert.False(e1.MoveNext());
@@ -192,6 +200,17 @@ namespace Microsoft.Framework.Primitives
[MemberData(nameof(FilledStringValuesWithExpected))]
public void Enumerator(StringValues stringValues, string[] expected)
{
+ var e = stringValues.GetEnumerator();
+ Assert.Null(e.Current);
+ for (int i = 0; i < expected.Length; i++)
+ {
+ Assert.True(e.MoveNext());
+ Assert.Equal(expected[i], e.Current);
+ }
+ Assert.False(e.MoveNext());
+ Assert.False(e.MoveNext());
+ Assert.False(e.MoveNext());
+
var e1 = ((IEnumerable)stringValues).GetEnumerator();
Assert.Null(e1.Current);
for (int i = 0; i < expected.Length; i++)
@@ -244,7 +263,13 @@ namespace Microsoft.Framework.Primitives
public void CopyTo(StringValues stringValues, string[] expected)
{
ICollection collection = stringValues;
+
+ string[] tooSmall = new string[0];
+ Assert.Throws(() => collection.CopyTo(tooSmall, 0));
+
string[] actual = new string[expected.Length];
+ Assert.Throws(() => collection.CopyTo(actual, -1));
+ Assert.Throws(() => collection.CopyTo(actual, actual.Length + 1));
collection.CopyTo(actual, 0);
Assert.Equal(expected, actual);
}