* Return singleton enumerators from IEnumerable.GetEnumerator for empty collections
Change the `IEnumerable<T>.GetEnumerator()` implementations on our core collection types to special-case Count==0 in order to return a single enumerator instead of allocating one a new each time. This saves an allocation when enumerating these collections via the interface in exchange for an extra length check as part of GetEnumerator.
* Address PR feedback
- Create helper function for empty enumerator
- Add tests for singletons
* Fix a few tests
/// </summary>
internal static partial class EnumerableHelpers
{
+ /// <summary>Gets an enumerator singleton for an empty collection.</summary>
+ internal static IEnumerator<T> GetEmptyEnumerator<T>() =>
+ ((IEnumerable<T>)Array.Empty<T>()).GetEnumerator();
+
/// <summary>Converts an enumerable to an array using the same logic as List{T}.</summary>
/// <param name="source">The enumerable to convert.</param>
/// <param name="length">The number of items stored in the resulting array, 0-indexed.</param>
ICollection<TKey> keys = dictionary.Keys;
IEnumerator<TKey> keysEnum = keys.GetEnumerator();
dictionary.Add(GetNewKey(dictionary), CreateTValue(3432));
- if (IDictionary_Generic_Keys_Values_Enumeration_ThrowsInvalidOperation_WhenParentModified)
+ if (count == 0 ? Enumerator_Empty_ModifiedDuringEnumeration_ThrowsInvalidOperationException : IDictionary_Generic_Keys_Values_Enumeration_ThrowsInvalidOperation_WhenParentModified)
{
Assert.Throws<InvalidOperationException>(() => keysEnum.MoveNext());
Assert.Throws<InvalidOperationException>(() => keysEnum.Reset());
ICollection<TValue> values = dictionary.Values;
IEnumerator<TValue> valuesEnum = values.GetEnumerator();
dictionary.Add(GetNewKey(dictionary), CreateTValue(3432));
- if (IDictionary_Generic_Keys_Values_Enumeration_ThrowsInvalidOperation_WhenParentModified)
+ if (count == 0 ? Enumerator_Empty_ModifiedDuringEnumeration_ThrowsInvalidOperationException : IDictionary_Generic_Keys_Values_Enumeration_ThrowsInvalidOperation_WhenParentModified)
{
Assert.Throws<InvalidOperationException>(() => valuesEnum.MoveNext());
Assert.Throws<InvalidOperationException>(() => valuesEnum.Reset());
ICollection keys = dictionary.Keys;
IEnumerator keysEnum = keys.GetEnumerator();
dictionary.Add(GetNewKey(dictionary), CreateTValue(3432));
- if (IDictionary_NonGeneric_Keys_Values_ParentDictionaryModifiedInvalidates)
+ if (count == 0 ? Enumerator_Empty_ModifiedDuringEnumeration_ThrowsInvalidOperationException : IDictionary_NonGeneric_Keys_Values_ParentDictionaryModifiedInvalidates)
{
Assert.Throws<InvalidOperationException>(() => keysEnum.MoveNext());
Assert.Throws<InvalidOperationException>(() => keysEnum.Reset());
ICollection values = dictionary.Values;
IEnumerator valuesEnum = values.GetEnumerator();
dictionary.Add(GetNewKey(dictionary), CreateTValue(3432));
- if (IDictionary_NonGeneric_Keys_Values_ParentDictionaryModifiedInvalidates)
+ if (count == 0 ? Enumerator_Empty_ModifiedDuringEnumeration_ThrowsInvalidOperationException : IDictionary_NonGeneric_Keys_Values_ParentDictionaryModifiedInvalidates)
{
Assert.Throws<InvalidOperationException>(() => valuesEnum.MoveNext());
Assert.Throws<InvalidOperationException>(() => valuesEnum.Reset());
protected virtual bool Enumerator_ModifiedDuringEnumeration_ThrowsInvalidOperationException => true;
/// <summary>
+ /// When calling MoveNext or Reset after modification of an empty enumeration, the resulting behavior is
+ /// undefined. Tests are included to cover two behavioral scenarios:
+ /// - Throwing an InvalidOperationException
+ /// - Execute MoveNext or Reset.
+ ///
+ /// If this property is set to true, the tests ensure that the exception is thrown. The default value is
+ /// <see cref="Enumerator_ModifiedDuringEnumeration_ThrowsInvalidOperationException"/>.
+ /// </summary>
+ protected virtual bool Enumerator_Empty_ModifiedDuringEnumeration_ThrowsInvalidOperationException => Enumerator_ModifiedDuringEnumeration_ThrowsInvalidOperationException;
+
+ /// <summary>Whether the enumerator returned from GetEnumerator is a singleton instance when the collection is empty.</summary>
+ protected virtual bool Enumerator_Empty_UsesSingletonInstance => false;
+
+ /// <summary>
/// Specifies whether this IEnumerable follows some sort of ordering pattern.
/// </summary>
protected virtual EnumerableOrder Order => EnumerableOrder.Sequential;
#region GetEnumerator()
+ [Fact]
+ public void IEnumerable_NonGeneric_GetEnumerator_EmptyCollection_UsesSingleton()
+ {
+ IEnumerable enumerable = GenericIEnumerableFactory(0);
+
+ IEnumerator enumerator1 = enumerable.GetEnumerator();
+ try
+ {
+ IEnumerator enumerator2 = enumerable.GetEnumerator();
+ try
+ {
+ Assert.Equal(Enumerator_Empty_UsesSingletonInstance, ReferenceEquals(enumerator1, enumerator2));
+ }
+ finally
+ {
+ if (enumerator2 is IDisposable d2) d2.Dispose();
+ }
+ }
+ finally
+ {
+ if (enumerator1 is IDisposable d1) d1.Dispose();
+ }
+ }
+
[Theory]
[MemberData(nameof(ValidCollectionSizes))]
public void IEnumerable_Generic_GetEnumerator_NoExceptionsWhileGetting(int count)
{
if (ModifyEnumerable(enumerable))
{
- if (Enumerator_ModifiedDuringEnumeration_ThrowsInvalidOperationException)
+ if (count == 0 ? Enumerator_Empty_ModifiedDuringEnumeration_ThrowsInvalidOperationException : Enumerator_ModifiedDuringEnumeration_ThrowsInvalidOperationException)
{
Assert.Throws<InvalidOperationException>(() => enumerator.MoveNext());
}
enumerator.MoveNext();
if (ModifyEnumerable(enumerable))
{
- if (Enumerator_ModifiedDuringEnumeration_ThrowsInvalidOperationException)
+ if (count == 0 ? Enumerator_Empty_ModifiedDuringEnumeration_ThrowsInvalidOperationException : Enumerator_ModifiedDuringEnumeration_ThrowsInvalidOperationException)
{
Assert.Throws<InvalidOperationException>(() => enumerator.MoveNext());
}
while (enumerator.MoveNext()) ;
if (ModifyEnumerable(enumerable))
{
- if (Enumerator_ModifiedDuringEnumeration_ThrowsInvalidOperationException)
+ if (count == 0 ? Enumerator_Empty_ModifiedDuringEnumeration_ThrowsInvalidOperationException : Enumerator_ModifiedDuringEnumeration_ThrowsInvalidOperationException)
{
Assert.Throws<InvalidOperationException>(() => enumerator.MoveNext());
}
IEnumerable<T> enumerable = GenericIEnumerableFactory(count);
using (IEnumerator<T> enumerator = enumerable.GetEnumerator())
{
- if (Enumerator_Current_UndefinedOperation_Throws ||
- (count == 0 && Enumerator_Empty_Current_UndefinedOperation_Throws))
+ if (count == 0 ? Enumerator_Empty_Current_UndefinedOperation_Throws : Enumerator_Current_UndefinedOperation_Throws)
Assert.Throws<InvalidOperationException>(() => enumerator.Current);
else
current = enumerator.Current;
using (IEnumerator<T> enumerator = enumerable.GetEnumerator())
{
while (enumerator.MoveNext()) ;
- if (Enumerator_Current_UndefinedOperation_Throws ||
- (count == 0 && Enumerator_Empty_Current_UndefinedOperation_Throws))
+ if (count == 0 ? Enumerator_Empty_Current_UndefinedOperation_Throws : Enumerator_Current_UndefinedOperation_Throws)
Assert.Throws<InvalidOperationException>(() => enumerator.Current);
else
current = enumerator.Current;
{
if (ModifyEnumerable(enumerable))
{
- if (Enumerator_Current_UndefinedOperation_Throws)
+ if (count == 0 ? Enumerator_Empty_Current_UndefinedOperation_Throws : Enumerator_Current_UndefinedOperation_Throws)
Assert.Throws<InvalidOperationException>(() => enumerator.Current);
else
current = enumerator.Current;
{
if (ModifyEnumerable(enumerable))
{
- if (Enumerator_ModifiedDuringEnumeration_ThrowsInvalidOperationException)
+ if (count == 0 ? Enumerator_Empty_ModifiedDuringEnumeration_ThrowsInvalidOperationException : Enumerator_ModifiedDuringEnumeration_ThrowsInvalidOperationException)
{
Assert.Throws<InvalidOperationException>(() => enumerator.Reset());
}
enumerator.MoveNext();
if (ModifyEnumerable(enumerable))
{
- if (Enumerator_ModifiedDuringEnumeration_ThrowsInvalidOperationException)
+ if (count == 0 ? Enumerator_Empty_ModifiedDuringEnumeration_ThrowsInvalidOperationException : Enumerator_ModifiedDuringEnumeration_ThrowsInvalidOperationException)
{
Assert.Throws<InvalidOperationException>(() => enumerator.Reset());
}
while (enumerator.MoveNext()) ;
if (ModifyEnumerable(enumerable))
{
- if (Enumerator_ModifiedDuringEnumeration_ThrowsInvalidOperationException)
+ if (count == 0 ? Enumerator_Empty_ModifiedDuringEnumeration_ThrowsInvalidOperationException : Enumerator_ModifiedDuringEnumeration_ThrowsInvalidOperationException)
{
Assert.Throws<InvalidOperationException>(() => enumerator.Reset());
}
protected virtual bool Enumerator_Current_UndefinedOperation_Throws => false;
/// <summary>
+ /// When calling MoveNext or Reset after modification of the enumeration, the resulting behavior is
+ /// undefined. Tests are included to cover two behavioral scenarios:
+ /// - Throwing an InvalidOperationException
+ /// - Execute MoveNext or Reset.
+ ///
+ /// If this property is set to true, the tests ensure that the exception is thrown. The default value is
+ /// true.
+ /// </summary>
+ protected virtual bool Enumerator_ModifiedDuringEnumeration_ThrowsInvalidOperationException => true;
+
+ /// <summary>
+ /// When calling MoveNext or Reset after modification of an empty enumeration, the resulting behavior is
+ /// undefined. Tests are included to cover two behavioral scenarios:
+ /// - Throwing an InvalidOperationException
+ /// - Execute MoveNext or Reset.
+ ///
+ /// If this property is set to true, the tests ensure that the exception is thrown. The default value is
+ /// <see cref="Enumerator_ModifiedDuringEnumeration_ThrowsInvalidOperationException"/>.
+ /// </summary>
+ protected virtual bool Enumerator_Empty_ModifiedDuringEnumeration_ThrowsInvalidOperationException => Enumerator_ModifiedDuringEnumeration_ThrowsInvalidOperationException;
+
+ /// <summary>Whether the enumerator returned from GetEnumerator is a singleton instance when the collection is empty.</summary>
+ protected virtual bool Enumerator_Empty_UsesSingletonInstance => false;
+
+ /// <summary>
/// Whether the collection can be serialized.
/// </summary>
protected virtual bool SupportsSerialization => true;
#region GetEnumerator()
+ [Fact]
+ public void IEnumerable_NonGeneric_GetEnumerator_EmptyCollection_UsesSingleton()
+ {
+ IEnumerable enumerable = NonGenericIEnumerableFactory(0);
+
+ IEnumerator enumerator1 = enumerable.GetEnumerator();
+ try
+ {
+ IEnumerator enumerator2 = enumerable.GetEnumerator();
+ try
+ {
+ Assert.Equal(Enumerator_Empty_UsesSingletonInstance, ReferenceEquals(enumerator1, enumerator2));
+ }
+ finally
+ {
+ if (enumerator2 is IDisposable d2) d2.Dispose();
+ }
+ }
+ finally
+ {
+ if (enumerator1 is IDisposable d1) d1.Dispose();
+ }
+ }
+
[Theory]
[MemberData(nameof(ValidCollectionSizes))]
public void IEnumerable_NonGeneric_GetEnumerator_NoExceptionsWhileGetting(int count)
IEnumerable enumerable = NonGenericIEnumerableFactory(count);
IEnumerator enumerator = enumerable.GetEnumerator();
if (ModifyEnumerable(enumerable))
- Assert.Throws<InvalidOperationException>(() => enumerator.MoveNext());
+ {
+ if (count == 0 ? Enumerator_Empty_ModifiedDuringEnumeration_ThrowsInvalidOperationException : Enumerator_ModifiedDuringEnumeration_ThrowsInvalidOperationException)
+ {
+ Assert.Throws<InvalidOperationException>(() => enumerator.MoveNext());
+ }
+ else
+ {
+ _ = enumerator.MoveNext();
+ }
+ }
});
}
{
IEnumerable enumerable = NonGenericIEnumerableFactory(count);
IEnumerator enumerator = enumerable.GetEnumerator();
+
for (int i = 0; i < count / 2; i++)
enumerator.MoveNext();
+
if (ModifyEnumerable(enumerable))
- Assert.Throws<InvalidOperationException>(() => enumerator.MoveNext());
+ {
+ if (count == 0 ? Enumerator_Empty_ModifiedDuringEnumeration_ThrowsInvalidOperationException : Enumerator_ModifiedDuringEnumeration_ThrowsInvalidOperationException)
+ {
+ Assert.Throws<InvalidOperationException>(() => enumerator.MoveNext());
+ }
+ else
+ {
+ enumerator.MoveNext();
+ }
+ }
});
}
IEnumerator enumerator = enumerable.GetEnumerator();
while (enumerator.MoveNext()) ;
if (ModifyEnumerable(enumerable))
- Assert.Throws<InvalidOperationException>(() => enumerator.MoveNext());
+ {
+ if (count == 0 ? Enumerator_Empty_ModifiedDuringEnumeration_ThrowsInvalidOperationException : Enumerator_ModifiedDuringEnumeration_ThrowsInvalidOperationException)
+ {
+ Assert.Throws<InvalidOperationException>(() => enumerator.MoveNext());
+ }
+ else
+ {
+ _ = enumerator.MoveNext();
+ }
+ }
});
}
IEnumerable enumerable = NonGenericIEnumerableFactory(count);
IEnumerator enumerator = enumerable.GetEnumerator();
if (ModifyEnumerable(enumerable))
- Assert.Throws<InvalidOperationException>(() => enumerator.Reset());
+ {
+ if (count == 0 ? Enumerator_Empty_ModifiedDuringEnumeration_ThrowsInvalidOperationException : Enumerator_ModifiedDuringEnumeration_ThrowsInvalidOperationException)
+ {
+ Assert.Throws<InvalidOperationException>(() => enumerator.Reset());
+ }
+ else
+ {
+ enumerator.Reset();
+ }
+ }
});
}
{
IEnumerable enumerable = NonGenericIEnumerableFactory(count);
IEnumerator enumerator = enumerable.GetEnumerator();
+
for (int i = 0; i < count / 2; i++)
enumerator.MoveNext();
+
if (ModifyEnumerable(enumerable))
- Assert.Throws<InvalidOperationException>(() => enumerator.Reset());
+ {
+ if (count == 0 ? Enumerator_Empty_ModifiedDuringEnumeration_ThrowsInvalidOperationException : Enumerator_ModifiedDuringEnumeration_ThrowsInvalidOperationException)
+ {
+ Assert.Throws<InvalidOperationException>(() => enumerator.Reset());
+ }
+ else
+ {
+ enumerator.Reset();
+ }
+ }
});
}
IEnumerator enumerator = enumerable.GetEnumerator();
while (enumerator.MoveNext()) ;
if (ModifyEnumerable(enumerable))
- Assert.Throws<InvalidOperationException>(() => enumerator.Reset());
+ {
+ if (count == 0 ? Enumerator_Empty_ModifiedDuringEnumeration_ThrowsInvalidOperationException : Enumerator_ModifiedDuringEnumeration_ThrowsInvalidOperationException)
+ {
+ Assert.Throws<InvalidOperationException>(() => enumerator.Reset());
+ }
+ else
+ {
+ enumerator.Reset();
+ }
+ }
});
}
while (enumerator.MoveNext()) ; // Go to end of enumerator
T current = default(T);
- if (Enumerator_Current_UndefinedOperation_Throws)
+ if (count == 0 ? Enumerator_Empty_Current_UndefinedOperation_Throws : Enumerator_Current_UndefinedOperation_Throws)
{
Assert.Throws<InvalidOperationException>(() => enumerator.Current); // enumerator.Current should fail
}
{
collection.Add(CreateT(seed++));
- if (Enumerator_Current_UndefinedOperation_Throws)
+ if (count == 0 ? Enumerator_Empty_Current_UndefinedOperation_Throws : Enumerator_Current_UndefinedOperation_Throws)
{
Assert.Throws<InvalidOperationException>(() => enumerator.Current); // enumerator.Current should fail
}
protected override IEnumerable<ModifyEnumerable> GetModifyEnumerables(ModifyOperation operations) => new List<ModifyEnumerable>();
+ protected override bool Enumerator_ModifiedDuringEnumeration_ThrowsInvalidOperationException => false;
protected override bool IDictionary_Generic_Keys_Values_Enumeration_ThrowsInvalidOperation_WhenParentModified => false;
protected override bool IDictionary_Generic_Keys_Values_ModifyingTheDictionaryUpdatesTheCollection => false;
protected override bool Enumerator_Current_UndefinedOperation_Throws => false;
+ protected override bool Enumerator_ModifiedDuringEnumeration_ThrowsInvalidOperationException => false;
+
protected override bool IDictionary_NonGeneric_Keys_Values_ModifyingTheDictionaryUpdatesTheCollection => false;
protected override bool ICollection_NonGeneric_SupportsSyncRoot => false;
protected override bool IsReadOnly => true;
protected override IEnumerable<ModifyEnumerable> GetModifyEnumerables(ModifyOperation operations) => new List<ModifyEnumerable>();
protected override Type IList_Generic_Item_InvalidIndex_ThrowType => typeof(IndexOutOfRangeException);
+ protected override bool Enumerator_Empty_UsesSingletonInstance => true;
protected override bool Enumerator_Current_UndefinedOperation_Throws => true;
protected override IList<T> GenericIListFactory() => GenericIListFactory(0);
protected override Type IList_NonGeneric_Item_InvalidIndex_ThrowType => typeof(IndexOutOfRangeException);
protected override IEnumerable<ModifyEnumerable> GetModifyEnumerables(ModifyOperation operations) => new List<ModifyEnumerable>();
+ protected override bool Enumerator_Empty_UsesSingletonInstance => true;
protected override bool Enumerator_Current_UndefinedOperation_Throws => true;
protected override Type ICollection_NonGeneric_CopyTo_TwoDimensionArray_ThrowType => typeof(RankException);
return null;
}
- public Enumerator GetEnumerator()
- {
- return new Enumerator(this);
- }
+ public Enumerator GetEnumerator() => new Enumerator(this);
- IEnumerator<T> IEnumerable<T>.GetEnumerator()
- {
- return GetEnumerator();
- }
+ IEnumerator<T> IEnumerable<T>.GetEnumerator() =>
+ Count == 0 ? EnumerableHelpers.GetEmptyEnumerator<T>() :
+ GetEnumerator();
public bool Remove(T value)
{
}
}
- IEnumerator IEnumerable.GetEnumerator()
- {
- return GetEnumerator();
- }
+ IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable<T>)this).GetEnumerator();
public struct Enumerator : IEnumerator<T>, IEnumerator, ISerializable, IDeserializationCallback
{
/// <returns>An <see cref="Enumerator"/> for the <see cref="UnorderedItems"/>.</returns>
public Enumerator GetEnumerator() => new Enumerator(_queue);
- IEnumerator<(TElement Element, TPriority Priority)> IEnumerable<(TElement Element, TPriority Priority)>.GetEnumerator() => GetEnumerator();
+ IEnumerator<(TElement Element, TPriority Priority)> IEnumerable<(TElement Element, TPriority Priority)>.GetEnumerator() =>
+ _queue.Count == 0 ? EnumerableHelpers.GetEmptyEnumerator<(TElement Element, TPriority Priority)>() :
+ GetEnumerator();
- IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+ IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable<(TElement Element, TPriority Priority)>)this).GetEnumerator();
}
}
}
_set.CopyTo(array, index);
}
- public Enumerator GetEnumerator()
- {
- return new Enumerator(this, Enumerator.KeyValuePair);
- }
+ public Enumerator GetEnumerator() => new Enumerator(this, Enumerator.KeyValuePair);
- IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()
- {
- return new Enumerator(this, Enumerator.KeyValuePair);
- }
+ IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator() =>
+ Count == 0 ? EnumerableHelpers.GetEmptyEnumerator<KeyValuePair<TKey, TValue>>() :
+ GetEnumerator();
public bool Remove(TKey key)
{
return (key is TKey);
}
- IDictionaryEnumerator IDictionary.GetEnumerator()
- {
- return new Enumerator(this, Enumerator.DictEntry);
- }
+ IDictionaryEnumerator IDictionary.GetEnumerator() => new Enumerator(this, Enumerator.DictEntry);
void IDictionary.Remove(object key)
{
get { return ((ICollection)_set).SyncRoot; }
}
- IEnumerator IEnumerable.GetEnumerator()
- {
- return new Enumerator(this, Enumerator.KeyValuePair);
- }
+ IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable<KeyValuePair<TKey, TValue>>)this).GetEnumerator();
public struct Enumerator : IEnumerator<KeyValuePair<TKey, TValue>>, IDictionaryEnumerator
{
_dictionary = dictionary;
}
- public Enumerator GetEnumerator()
- {
- return new Enumerator(_dictionary);
- }
+ public Enumerator GetEnumerator() => new Enumerator(_dictionary);
- IEnumerator<TKey> IEnumerable<TKey>.GetEnumerator()
- {
- return new Enumerator(_dictionary);
- }
+ IEnumerator<TKey> IEnumerable<TKey>.GetEnumerator() =>
+ Count == 0 ? EnumerableHelpers.GetEmptyEnumerator<TKey>() :
+ GetEnumerator();
- IEnumerator IEnumerable.GetEnumerator()
- {
- return new Enumerator(_dictionary);
- }
+ IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable<TKey>)this).GetEnumerator();
public void CopyTo(TKey[] array, int index)
{
_dictionary = dictionary;
}
- public Enumerator GetEnumerator()
- {
- return new Enumerator(_dictionary);
- }
+ public Enumerator GetEnumerator() => new Enumerator(_dictionary);
- IEnumerator<TValue> IEnumerable<TValue>.GetEnumerator()
- {
- return new Enumerator(_dictionary);
- }
+ IEnumerator<TValue> IEnumerable<TValue>.GetEnumerator() =>
+ Count == 0 ? EnumerableHelpers.GetEmptyEnumerator<TValue>() :
+ GetEnumerator();
- IEnumerator IEnumerable.GetEnumerator()
- {
- return new Enumerator(_dictionary);
- }
+ IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable<TValue>)this).GetEnumerator();
public void CopyTo(TValue[] array, int index)
{
version++;
}
- public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
- {
- return new Enumerator(this, Enumerator.KeyValuePair);
- }
+ public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() => new Enumerator(this, Enumerator.KeyValuePair);
- IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()
- {
- return new Enumerator(this, Enumerator.KeyValuePair);
- }
+ IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator() =>
+ Count == 0 ? EnumerableHelpers.GetEmptyEnumerator<KeyValuePair<TKey, TValue>>() :
+ GetEnumerator();
- IDictionaryEnumerator IDictionary.GetEnumerator()
- {
- return new Enumerator(this, Enumerator.DictEntry);
- }
+ IDictionaryEnumerator IDictionary.GetEnumerator() => new Enumerator(this, Enumerator.DictEntry);
- IEnumerator IEnumerable.GetEnumerator()
- {
- return new Enumerator(this, Enumerator.KeyValuePair);
- }
+ IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable<KeyValuePair<TKey, TValue>>)this).GetEnumerator();
/// <summary>
/// Gets the key corresponding to the specified index.
}
}
- public IEnumerator<TKey> GetEnumerator()
- {
- return new SortedListKeyEnumerator(_dict);
- }
+ public IEnumerator<TKey> GetEnumerator() =>
+ Count == 0 ? EnumerableHelpers.GetEmptyEnumerator<TKey>() :
+ new SortedListKeyEnumerator(_dict);
- IEnumerator IEnumerable.GetEnumerator()
- {
- return new SortedListKeyEnumerator(_dict);
- }
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public int IndexOf(TKey key)
{
}
}
- public IEnumerator<TValue> GetEnumerator()
- {
- return new SortedListValueEnumerator(_dict);
- }
+ public IEnumerator<TValue> GetEnumerator() =>
+ Count == 0 ? EnumerableHelpers.GetEmptyEnumerator<TValue>() :
+ new SortedListValueEnumerator(_dict);
- IEnumerator IEnumerable.GetEnumerator()
- {
- return new SortedListValueEnumerator(_dict);
- }
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public int IndexOf(TValue value)
{
IEnumerator<T> IEnumerable<T>.GetEnumerator() => GetEnumerator();
- IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+ IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable<T>)this).GetEnumerator();
#endregion
}
// Returns an IEnumerator for this Stack.
- public Enumerator GetEnumerator()
- {
- return new Enumerator(this);
- }
+ public Enumerator GetEnumerator() => new Enumerator(this);
/// <internalonly/>
- IEnumerator<T> IEnumerable<T>.GetEnumerator()
- {
- return new Enumerator(this);
- }
+ IEnumerator<T> IEnumerable<T>.GetEnumerator() =>
+ Count == 0 ? EnumerableHelpers.GetEmptyEnumerator<T>() :
+ GetEnumerator();
- IEnumerator IEnumerable.GetEnumerator()
- {
- return new Enumerator(this);
- }
+ IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable<T>)this).GetEnumerator();
public void TrimExcess()
{
{
public class Dictionary_Generic_Tests_Keys : ICollection_Generic_Tests<string>
{
+ protected override bool Enumerator_Empty_UsesSingletonInstance => true;
+ protected override bool Enumerator_Empty_Current_UndefinedOperation_Throws => true;
protected override bool DefaultValueAllowed => false;
protected override bool DuplicateValuesAllowed => false;
protected override bool IsReadOnly => true;
protected override bool NullAllowed => false;
protected override bool DuplicateValuesAllowed => false;
protected override bool IsReadOnly => true;
+ protected override bool Enumerator_Empty_UsesSingletonInstance => true;
protected override bool Enumerator_Current_UndefinedOperation_Throws => true;
protected override Type ICollection_NonGeneric_CopyTo_ArrayOfEnumType_ThrowType => typeof(ArgumentException);
protected override bool SupportsSerialization => false;
protected override bool DuplicateValuesAllowed => true;
protected override bool IsReadOnly => true;
protected override IEnumerable<ModifyEnumerable> GetModifyEnumerables(ModifyOperation operations) => new List<ModifyEnumerable>();
+ protected override bool Enumerator_Empty_UsesSingletonInstance => true;
+ protected override bool Enumerator_Empty_ModifiedDuringEnumeration_ThrowsInvalidOperationException => false;
+ protected override bool Enumerator_Empty_Current_UndefinedOperation_Throws => true;
protected override ICollection<string> GenericICollectionFactory()
{
protected override bool NullAllowed => true;
protected override bool DuplicateValuesAllowed => true;
protected override bool IsReadOnly => true;
+ protected override bool Enumerator_Empty_UsesSingletonInstance => true;
protected override bool Enumerator_Current_UndefinedOperation_Throws => true;
protected override Type ICollection_NonGeneric_CopyTo_ArrayOfEnumType_ThrowType => typeof(ArgumentException);
protected override IEnumerable<ModifyEnumerable> GetModifyEnumerables(ModifyOperation operations) => new List<ModifyEnumerable>();
protected override bool SupportsSerialization => false;
+ protected override bool Enumerator_Empty_ModifiedDuringEnumeration_ThrowsInvalidOperationException => false;
protected override Type ICollection_NonGeneric_CopyTo_IndexLargerThanArrayCount_ThrowType => typeof(ArgumentOutOfRangeException);
/// </summary>
public abstract class Dictionary_Generic_Tests<TKey, TValue> : IDictionary_Generic_Tests<TKey, TValue>
{
+ protected override bool Enumerator_Empty_UsesSingletonInstance => true;
+ protected override bool Enumerator_Empty_Current_UndefinedOperation_Throws => true;
+ protected override bool Enumerator_Empty_ModifiedDuringEnumeration_ThrowsInvalidOperationException => false;
+
protected override ModifyOperation ModifyEnumeratorThrows => ModifyOperation.Add | ModifyOperation.Insert;
protected override ModifyOperation ModifyEnumeratorAllowed => ModifyOperation.Overwrite | ModifyOperation.Remove | ModifyOperation.Clear;
{
public partial class Dictionary_IDictionary_NonGeneric_Tests : IDictionary_NonGeneric_Tests
{
+ protected override bool Enumerator_Empty_UsesSingletonInstance => true;
+ protected override bool Enumerator_Empty_ModifiedDuringEnumeration_ThrowsInvalidOperationException => false;
+
protected override IDictionary NonGenericIDictionaryFactory()
{
return new Dictionary<string, string>();
return set;
}
+ protected override bool Enumerator_Empty_UsesSingletonInstance => true;
protected override bool Enumerator_Current_UndefinedOperation_Throws => true;
protected override ModifyOperation ModifyEnumeratorThrows => PlatformDetection.IsNetFramework ? base.ModifyEnumeratorThrows : (base.ModifyEnumeratorAllowed & ~ModifyOperation.Remove);
public abstract class HashSet_Generic_Tests<T> : ISet_Generic_Tests<T>
{
#region ISet<T> Helper Methods
+ protected override bool Enumerator_Empty_UsesSingletonInstance => true;
+ protected override bool Enumerator_Empty_Current_UndefinedOperation_Throws => true;
protected override bool ResetImplemented => true;
{
public class LinkedList_ICollection_NonGeneric_Tests : ICollection_NonGeneric_Tests
{
+ protected override bool Enumerator_Empty_UsesSingletonInstance => true;
+ protected override bool Enumerator_Empty_ModifiedDuringEnumeration_ThrowsInvalidOperationException => false;
+
protected override Type ICollection_NonGeneric_CopyTo_ArrayOfEnumType_ThrowType => typeof(ArgumentException);
protected override void AddToCollection(ICollection collection, int numberOfItemsToAdd)
public abstract partial class LinkedList_Generic_Tests<T> : ICollection_Generic_Tests<T>
{
#region ICollection<T> Helper Methods
+ protected override bool Enumerator_Empty_UsesSingletonInstance => true;
+ protected override bool Enumerator_Empty_Current_UndefinedOperation_Throws => true;
+ protected override bool Enumerator_Empty_ModifiedDuringEnumeration_ThrowsInvalidOperationException => false;
protected override ICollection<T> GenericICollectionFactory()
{
public abstract partial class List_Generic_Tests<T> : IList_Generic_Tests<T>
{
#region IList<T> Helper Methods
+ protected override bool Enumerator_Empty_UsesSingletonInstance => true;
+ protected override bool Enumerator_Empty_Current_UndefinedOperation_Throws => true;
+ protected override bool Enumerator_Empty_ModifiedDuringEnumeration_ThrowsInvalidOperationException => false;
protected override IList<T> GenericIListFactory()
{
protected override bool Contains(IEnumerable<T> enumerable, T value) => ((Queue<T>)enumerable).Contains(value);
protected override void CopyTo(IEnumerable<T> enumerable, T[] array, int index) => ((Queue<T>)enumerable).CopyTo(array, index);
protected override bool Remove(IEnumerable<T> enumerable) => ((Queue<T>)enumerable).TryDequeue(out _);
+ protected override bool Enumerator_Empty_UsesSingletonInstance => true;
protected override bool Enumerator_Current_UndefinedOperation_Throws => true;
-
+ protected override bool Enumerator_Empty_ModifiedDuringEnumeration_ThrowsInvalidOperationException => false;
protected override Type IGenericSharedAPI_CopyTo_IndexLargerThanArrayCount_ThrowType => typeof(ArgumentOutOfRangeException);
#endregion
public class Queue_ICollection_NonGeneric_Tests : ICollection_NonGeneric_Tests
{
#region ICollection Helper Methods
+ protected override bool Enumerator_Empty_UsesSingletonInstance => true;
+ protected override bool Enumerator_Empty_ModifiedDuringEnumeration_ThrowsInvalidOperationException => false;
protected override Type ICollection_NonGeneric_CopyTo_ArrayOfEnumType_ThrowType => typeof(ArgumentException);
{
public class SortedDictionary_Generic_Tests_Keys : ICollection_Generic_Tests<string>
{
+ protected override bool Enumerator_Empty_UsesSingletonInstance => true;
+ protected override bool Enumerator_Empty_Current_UndefinedOperation_Throws => true;
protected override bool DefaultValueAllowed => false;
protected override bool DuplicateValuesAllowed => false;
protected override bool IsReadOnly => true;
protected override bool NullAllowed => false;
protected override bool DuplicateValuesAllowed => false;
protected override bool IsReadOnly => true;
+ protected override bool Enumerator_Empty_UsesSingletonInstance => true;
protected override bool Enumerator_Current_UndefinedOperation_Throws => true;
protected override IEnumerable<ModifyEnumerable> GetModifyEnumerables(ModifyOperation operations) => new List<ModifyEnumerable>();
protected override ICollection NonGenericICollectionFactory()
{
public class SortedDictionary_Generic_Tests_Values : ICollection_Generic_Tests<string>
{
+ protected override bool Enumerator_Empty_UsesSingletonInstance => true;
+ protected override bool Enumerator_Empty_Current_UndefinedOperation_Throws => true;
protected override bool DefaultValueAllowed => true;
protected override bool DuplicateValuesAllowed => true;
protected override bool IsReadOnly => true;
protected override bool NullAllowed => true;
protected override bool DuplicateValuesAllowed => true;
protected override bool IsReadOnly => true;
+ protected override bool Enumerator_Empty_UsesSingletonInstance => true;
protected override bool Enumerator_Current_UndefinedOperation_Throws => true;
protected override IEnumerable<ModifyEnumerable> GetModifyEnumerables(ModifyOperation operations) => new List<ModifyEnumerable>();
protected override bool SupportsSerialization => false;
public abstract class SortedDictionary_Generic_Tests<TKey, TValue> : IDictionary_Generic_Tests<TKey, TValue>
{
#region IDictionary<TKey, TValue> Helper Methods
-
+ protected override bool Enumerator_Empty_UsesSingletonInstance => true;
+ protected override bool Enumerator_Empty_Current_UndefinedOperation_Throws => true;
+ protected override bool Enumerator_Empty_ModifiedDuringEnumeration_ThrowsInvalidOperationException => false;
protected override bool DefaultValueWhenNotAllowed_Throws { get { return false; } }
protected override IDictionary<TKey, TValue> GenericIDictionaryFactory()
public class SortedDictionary_IDictionary_NonGeneric_Tests : IDictionary_NonGeneric_Tests
{
#region IDictionary Helper Methods
+ protected override bool Enumerator_Empty_UsesSingletonInstance => true;
+ protected override bool Enumerator_Empty_ModifiedDuringEnumeration_ThrowsInvalidOperationException => false;
protected override IDictionary NonGenericIDictionaryFactory()
{
{
public class SortedList_Generic_Tests_Keys : IList_Generic_Tests<string>
{
+ protected override bool Enumerator_Empty_UsesSingletonInstance => true;
+ protected override bool Enumerator_Empty_Current_UndefinedOperation_Throws => true;
+ protected override bool Enumerator_Empty_ModifiedDuringEnumeration_ThrowsInvalidOperationException => false;
protected override bool DefaultValueAllowed => false;
protected override bool DuplicateValuesAllowed => false;
protected override bool IsReadOnly => true;
protected override bool NullAllowed => false;
protected override bool DuplicateValuesAllowed => false;
protected override bool IsReadOnly => true;
+ protected override bool Enumerator_Empty_UsesSingletonInstance => true;
protected override bool Enumerator_Current_UndefinedOperation_Throws => true;
protected override bool SupportsSerialization => false;
{
public class SortedList_Generic_Tests_Values : IList_Generic_Tests<string>
{
+ protected override bool Enumerator_Empty_UsesSingletonInstance => true;
+ protected override bool Enumerator_Empty_Current_UndefinedOperation_Throws => true;
+ protected override bool Enumerator_Empty_ModifiedDuringEnumeration_ThrowsInvalidOperationException => false;
protected override bool DefaultValueAllowed => true;
protected override bool DuplicateValuesAllowed => true;
protected override bool IsReadOnly => true;
protected override bool NullAllowed => true;
protected override bool DuplicateValuesAllowed => true;
protected override bool IsReadOnly => true;
+ protected override bool Enumerator_Empty_UsesSingletonInstance => true;
protected override bool Enumerator_Current_UndefinedOperation_Throws => true;
protected override bool SupportsSerialization => false;
public abstract class SortedList_Generic_Tests<TKey, TValue> : IDictionary_Generic_Tests<TKey, TValue>
{
#region IDictionary<TKey, TValue> Helper Methods
+ protected override bool Enumerator_Empty_UsesSingletonInstance => true;
+ protected override bool Enumerator_Empty_Current_UndefinedOperation_Throws => true;
+ protected override bool Enumerator_Empty_ModifiedDuringEnumeration_ThrowsInvalidOperationException => false;
protected override IDictionary<TKey, TValue> GenericIDictionaryFactory()
{
public class SortedList_IDictionary_NonGeneric_Tests : IDictionary_NonGeneric_Tests
{
#region IDictionary Helper Methods
-
+ protected override bool Enumerator_Empty_UsesSingletonInstance => true;
+ protected override bool Enumerator_Empty_ModifiedDuringEnumeration_ThrowsInvalidOperationException => false;
protected override IDictionary NonGenericIDictionaryFactory()
{
return new SortedList<string, string>();
protected override Type IGenericSharedAPI_CopyTo_IndexLargerThanArrayCount_ThrowType => typeof(ArgumentOutOfRangeException);
+ protected override bool Enumerator_Empty_UsesSingletonInstance => true;
+ protected override bool Enumerator_Empty_ModifiedDuringEnumeration_ThrowsInvalidOperationException => false;
+
#endregion
protected override IEnumerable<T> GenericIEnumerableFactory()
return new Stack<string>();
}
+ protected override bool Enumerator_Empty_UsesSingletonInstance => true;
protected override bool Enumerator_Current_UndefinedOperation_Throws => true;
+ protected override bool Enumerator_Empty_ModifiedDuringEnumeration_ThrowsInvalidOperationException => false;
protected override Type ICollection_NonGeneric_CopyTo_IndexLargerThanArrayCount_ThrowType => typeof(ArgumentOutOfRangeException);
#region IEnumerable<T>
- IEnumerator<T> IEnumerable<T>.GetEnumerator() => GetEnumerator();
+ IEnumerator<T> IEnumerable<T>.GetEnumerator()
+ {
+ ThrowInvalidOperationIfDefault();
+ return
+ Count == 0 ? SZGenericArrayEnumerator<T>.Empty :
+ new Enumerator(this);
+ }
#endregion
#region IEnumerable
- IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+ IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable<T>)this).GetEnumerator();
#endregion
private void ThrowInvalidOperationIfDefault()
public Enumerator GetEnumerator() => new Enumerator(this, Enumerator.KeyValuePair);
IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator() =>
- new Enumerator(this, Enumerator.KeyValuePair);
+ Count == 0 ? SZGenericArrayEnumerator<KeyValuePair<TKey, TValue>>.Empty :
+ GetEnumerator();
public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
{
}
}
- IEnumerator IEnumerable.GetEnumerator() => new Enumerator(this, Enumerator.KeyValuePair);
+ IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable<KeyValuePair<TKey, TValue>>)this).GetEnumerator();
/// <summary>
/// Ensures that the dictionary can hold up to 'capacity' entries without any further expansion of its backing storage
return false;
}
- IEnumerator<TKey> IEnumerable<TKey>.GetEnumerator() => new Enumerator(_dictionary);
+ IEnumerator<TKey> IEnumerable<TKey>.GetEnumerator() =>
+ Count == 0 ? SZGenericArrayEnumerator<TKey>.Empty :
+ GetEnumerator();
- IEnumerator IEnumerable.GetEnumerator() => new Enumerator(_dictionary);
+ IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable<TKey>)this).GetEnumerator();
void ICollection.CopyTo(Array array, int index)
{
bool ICollection<TValue>.Contains(TValue item) => _dictionary.ContainsValue(item);
- IEnumerator<TValue> IEnumerable<TValue>.GetEnumerator() => new Enumerator(_dictionary);
+ IEnumerator<TValue> IEnumerable<TValue>.GetEnumerator() =>
+ Count == 0 ? SZGenericArrayEnumerator<TValue>.Empty :
+ GetEnumerator();
- IEnumerator IEnumerable.GetEnumerator() => new Enumerator(_dictionary);
+ IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable<TValue>)this).GetEnumerator();
void ICollection.CopyTo(Array array, int index)
{
public Enumerator GetEnumerator() => new Enumerator(this);
- IEnumerator<T> IEnumerable<T>.GetEnumerator() => GetEnumerator();
+ IEnumerator<T> IEnumerable<T>.GetEnumerator() =>
+ Count == 0 ? SZGenericArrayEnumerator<T>.Empty :
+ GetEnumerator();
- IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+ IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable<T>)this).GetEnumerator();
#endregion
// while an enumeration is in progress, the MoveNext and
// GetObject methods of the enumerator will throw an exception.
//
- public Enumerator GetEnumerator()
- => new Enumerator(this);
+ public Enumerator GetEnumerator() => new Enumerator(this);
- IEnumerator<T> IEnumerable<T>.GetEnumerator()
- => new Enumerator(this);
+ IEnumerator<T> IEnumerable<T>.GetEnumerator() =>
+ Count == 0 ? SZGenericArrayEnumerator<T>.Empty :
+ GetEnumerator();
- IEnumerator IEnumerable.GetEnumerator()
- => new Enumerator(this);
+ IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable<T>)this).GetEnumerator();
public List<T> GetRange(int index, int count)
{
public struct Enumerator : IEnumerator<T>, IEnumerator
{
+ internal static IEnumerator<T>? s_emptyEnumerator;
+
private readonly List<T> _list;
private int _index;
private readonly int _version;
// GetEnumerator returns an IEnumerator over this Queue. This
// Enumerator will support removing.
- public Enumerator GetEnumerator()
- {
- return new Enumerator(this);
- }
+ public Enumerator GetEnumerator() => new Enumerator(this);
/// <internalonly/>
- IEnumerator<T> IEnumerable<T>.GetEnumerator()
- {
- return new Enumerator(this);
- }
+ IEnumerator<T> IEnumerable<T>.GetEnumerator() =>
+ Count == 0 ? SZGenericArrayEnumerator<T>.Empty :
+ GetEnumerator();
- IEnumerator IEnumerable.GetEnumerator()
- {
- return new Enumerator(this);
- }
+ IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable<T>)this).GetEnumerator();
// Removes the object at the head of the queue and returns it. If the queue
// is empty, this method throws an
}
public IEnumerator<T> GetEnumerator() =>
- list.Count == 0 ?
- SZGenericArrayEnumerator<T>.Empty :
- list.GetEnumerator();
+ list.Count == 0 ? SZGenericArrayEnumerator<T>.Empty :
+ list.GetEnumerator();
public int IndexOf(T value)
{
{
Container c = _container;
return c is null || c.FirstFreeEntry == 0 ?
- ((IEnumerable<KeyValuePair<TKey, TValue>>)Array.Empty<KeyValuePair<TKey, TValue>>()).GetEnumerator() :
+ SZGenericArrayEnumerator<KeyValuePair<TKey, TValue>>.Empty :
new Enumerator(this);
}
}
return Factory(count * 2, count / 2, count);
}
+ protected override bool Enumerator_Empty_UsesSingletonInstance => true;
protected override bool Enumerator_Current_UndefinedOperation_Throws => true;
protected override bool Enumerator_ModifiedDuringEnumeration_ThrowsInvalidOperationException => false;
protected override bool IsReadOnly_ValidityValue => true;