Return singleton enumerators from IEnumerable.GetEnumerator for empty collections...
authorStephen Toub <stoub@microsoft.com>
Thu, 23 Feb 2023 23:00:59 +0000 (18:00 -0500)
committerGitHub <noreply@github.com>
Thu, 23 Feb 2023 23:00:59 +0000 (18:00 -0500)
* 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

45 files changed:
src/libraries/Common/src/System/Collections/Generic/EnumerableHelpers.cs
src/libraries/Common/tests/System/Collections/IDictionary.Generic.Tests.cs
src/libraries/Common/tests/System/Collections/IDictionary.NonGeneric.Tests.cs
src/libraries/Common/tests/System/Collections/IEnumerable.Generic.Tests.cs
src/libraries/Common/tests/System/Collections/IEnumerable.NonGeneric.Tests.cs
src/libraries/Common/tests/System/Collections/IList.Generic.Tests.cs
src/libraries/System.Collections.Concurrent/tests/ConcurrentDictionary/ConcurrentDictionary.Generic.Tests.cs
src/libraries/System.Collections.Concurrent/tests/ConcurrentDictionary/ConcurrentDictionary.NonGeneric.Tests.cs
src/libraries/System.Collections.Immutable/tests/ImmutableArray/ImmutableArray.Generic.Tests.cs
src/libraries/System.Collections.Immutable/tests/ImmutableArray/ImmutableArray.NonGeneric.Tests.cs
src/libraries/System.Collections/src/System/Collections/Generic/LinkedList.cs
src/libraries/System.Collections/src/System/Collections/Generic/PriorityQueue.cs
src/libraries/System.Collections/src/System/Collections/Generic/SortedDictionary.cs
src/libraries/System.Collections/src/System/Collections/Generic/SortedList.cs
src/libraries/System.Collections/src/System/Collections/Generic/SortedSet.cs
src/libraries/System.Collections/src/System/Collections/Generic/Stack.cs
src/libraries/System.Collections/tests/Generic/Dictionary/Dictionary.Generic.Tests.Keys.cs
src/libraries/System.Collections/tests/Generic/Dictionary/Dictionary.Generic.Tests.Values.cs
src/libraries/System.Collections/tests/Generic/Dictionary/Dictionary.Generic.Tests.cs
src/libraries/System.Collections/tests/Generic/Dictionary/Dictionary.Tests.cs
src/libraries/System.Collections/tests/Generic/HashSet/HashSet.Generic.Tests.AsNonGenericIEnumerable.cs
src/libraries/System.Collections/tests/Generic/HashSet/HashSet.Generic.Tests.cs
src/libraries/System.Collections/tests/Generic/LinkedList/LinkedList.Generic.Tests.AsNonGenericICollection.cs
src/libraries/System.Collections/tests/Generic/LinkedList/LinkedList.Generic.Tests.cs
src/libraries/System.Collections/tests/Generic/List/List.Generic.Tests.cs
src/libraries/System.Collections/tests/Generic/Queue/Queue.Generic.Tests.cs
src/libraries/System.Collections/tests/Generic/Queue/Queue.Tests.cs
src/libraries/System.Collections/tests/Generic/SortedDictionary/SortedDictionary.Generic.Tests.Keys.cs
src/libraries/System.Collections/tests/Generic/SortedDictionary/SortedDictionary.Generic.Tests.Values.cs
src/libraries/System.Collections/tests/Generic/SortedDictionary/SortedDictionary.Generic.Tests.cs
src/libraries/System.Collections/tests/Generic/SortedDictionary/SortedDictionary.Tests.cs
src/libraries/System.Collections/tests/Generic/SortedList/SortedList.Generic.Tests.Keys.cs
src/libraries/System.Collections/tests/Generic/SortedList/SortedList.Generic.Tests.Values.cs
src/libraries/System.Collections/tests/Generic/SortedList/SortedList.Generic.Tests.cs
src/libraries/System.Collections/tests/Generic/SortedList/SortedList.Tests.cs
src/libraries/System.Collections/tests/Generic/Stack/Stack.Generic.Tests.cs
src/libraries/System.Collections/tests/Generic/Stack/Stack.Tests.cs
src/libraries/System.Private.CoreLib/src/System/ArraySegment.cs
src/libraries/System.Private.CoreLib/src/System/Collections/Generic/Dictionary.cs
src/libraries/System.Private.CoreLib/src/System/Collections/Generic/HashSet.cs
src/libraries/System.Private.CoreLib/src/System/Collections/Generic/List.cs
src/libraries/System.Private.CoreLib/src/System/Collections/Generic/Queue.cs
src/libraries/System.Private.CoreLib/src/System/Collections/ObjectModel/ReadOnlyCollection.cs
src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ConditionalWeakTable.cs
src/libraries/System.Runtime/tests/System/ArraySegmentTests.cs

index b8646e3..f73a2d3 100644 (file)
@@ -8,6 +8,10 @@ namespace System.Collections.Generic
     /// </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>
index 44e28ea..22f3912 100644 (file)
@@ -421,7 +421,7 @@ namespace System.Collections.Tests
                 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());
@@ -528,7 +528,7 @@ namespace System.Collections.Tests
                 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());
index 944d067..adca278 100644 (file)
@@ -392,7 +392,7 @@ namespace System.Collections.Tests
                 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());
@@ -487,7 +487,7 @@ namespace System.Collections.Tests
                 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());
index c23f9ca..a71f72e 100644 (file)
@@ -86,6 +86,20 @@ namespace System.Collections.Tests
         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;
@@ -302,6 +316,30 @@ namespace System.Collections.Tests
 
         #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)
@@ -381,7 +419,7 @@ namespace System.Collections.Tests
                 {
                     if (ModifyEnumerable(enumerable))
                     {
-                        if (Enumerator_ModifiedDuringEnumeration_ThrowsInvalidOperationException)
+                        if (count == 0 ? Enumerator_Empty_ModifiedDuringEnumeration_ThrowsInvalidOperationException : Enumerator_ModifiedDuringEnumeration_ThrowsInvalidOperationException)
                         {
                             Assert.Throws<InvalidOperationException>(() => enumerator.MoveNext());
                         }
@@ -427,7 +465,7 @@ namespace System.Collections.Tests
                         enumerator.MoveNext();
                     if (ModifyEnumerable(enumerable))
                     {
-                        if (Enumerator_ModifiedDuringEnumeration_ThrowsInvalidOperationException)
+                        if (count == 0 ? Enumerator_Empty_ModifiedDuringEnumeration_ThrowsInvalidOperationException : Enumerator_ModifiedDuringEnumeration_ThrowsInvalidOperationException)
                         {
                             Assert.Throws<InvalidOperationException>(() => enumerator.MoveNext());
                         }
@@ -471,7 +509,7 @@ namespace System.Collections.Tests
                     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());
                         }
@@ -602,8 +640,7 @@ namespace System.Collections.Tests
             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;
@@ -619,8 +656,7 @@ namespace System.Collections.Tests
             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;
@@ -639,7 +675,7 @@ namespace System.Collections.Tests
                 {
                     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;
@@ -694,7 +730,7 @@ namespace System.Collections.Tests
                 {
                     if (ModifyEnumerable(enumerable))
                     {
-                        if (Enumerator_ModifiedDuringEnumeration_ThrowsInvalidOperationException)
+                        if (count == 0 ? Enumerator_Empty_ModifiedDuringEnumeration_ThrowsInvalidOperationException : Enumerator_ModifiedDuringEnumeration_ThrowsInvalidOperationException)
                         {
                             Assert.Throws<InvalidOperationException>(() => enumerator.Reset());
                         }
@@ -737,7 +773,7 @@ namespace System.Collections.Tests
                         enumerator.MoveNext();
                     if (ModifyEnumerable(enumerable))
                     {
-                        if (Enumerator_ModifiedDuringEnumeration_ThrowsInvalidOperationException)
+                        if (count == 0 ? Enumerator_Empty_ModifiedDuringEnumeration_ThrowsInvalidOperationException : Enumerator_ModifiedDuringEnumeration_ThrowsInvalidOperationException)
                         {
                             Assert.Throws<InvalidOperationException>(() => enumerator.Reset());
                         }
@@ -781,7 +817,7 @@ namespace System.Collections.Tests
                     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());
                         }
index 5760cbe..a441cf4 100644 (file)
@@ -61,6 +61,31 @@ namespace System.Collections.Tests
         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;
@@ -83,6 +108,30 @@ namespace System.Collections.Tests
 
         #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)
@@ -141,7 +190,16 @@ namespace System.Collections.Tests
                 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();
+                    }
+                }
             });
         }
 
@@ -153,10 +211,21 @@ namespace System.Collections.Tests
             {
                 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();
+                    }
+                }
             });
         }
 
@@ -170,7 +239,16 @@ namespace System.Collections.Tests
                 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();
+                    }
+                }
             });
         }
 
@@ -290,7 +368,16 @@ namespace System.Collections.Tests
                 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();
+                    }
+                }
             });
         }
 
@@ -302,10 +389,21 @@ namespace System.Collections.Tests
             {
                 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();
+                    }
+                }
             });
         }
 
@@ -319,7 +417,16 @@ namespace System.Collections.Tests
                 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();
+                    }
+                }
             });
         }
 
index 820ea64..44f67e0 100644 (file)
@@ -614,7 +614,7 @@ namespace System.Collections.Tests
                     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
                     }
@@ -630,7 +630,7 @@ namespace System.Collections.Tests
                     {
                         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
                         }
index 1ec173d..f488444 100644 (file)
@@ -189,6 +189,7 @@ namespace System.Collections.Concurrent.Tests
 
         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;
index 9df2101..b7e63f5 100644 (file)
@@ -38,6 +38,8 @@ namespace System.Collections.Concurrent.Tests
 
         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;
index f1373db..a21bcf8 100644 (file)
@@ -20,6 +20,7 @@ namespace System.Collections.Immutable.Tests
         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);
index 0d9410c..fabefa1 100644 (file)
@@ -15,6 +15,7 @@ namespace System.Collections.Immutable.Tests
 
         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);
index 0e99312..a50dce3 100644 (file)
@@ -283,15 +283,11 @@ namespace System.Collections.Generic
             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)
         {
@@ -497,10 +493,7 @@ namespace System.Collections.Generic
             }
         }
 
-        IEnumerator IEnumerable.GetEnumerator()
-        {
-            return GetEnumerator();
-        }
+        IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable<T>)this).GetEnumerator();
 
         public struct Enumerator : IEnumerator<T>, IEnumerator, ISerializable, IDeserializationCallback
         {
index 67f2ae3..e8d8221 100644 (file)
@@ -961,9 +961,11 @@ namespace System.Collections.Generic
             /// <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();
         }
     }
 }
index 0833522..64d745a 100644 (file)
@@ -247,15 +247,11 @@ namespace System.Collections.Generic
             _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)
         {
@@ -389,10 +385,7 @@ namespace System.Collections.Generic
             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)
         {
@@ -412,10 +405,7 @@ namespace System.Collections.Generic
             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
         {
@@ -541,20 +531,13 @@ namespace System.Collections.Generic
                 _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)
             {
@@ -709,20 +692,13 @@ namespace System.Collections.Generic
                 _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)
             {
index 3204094..167e1be 100644 (file)
@@ -542,25 +542,15 @@ namespace System.Collections.Generic
             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.
@@ -1091,15 +1081,11 @@ namespace System.Collections.Generic
                 }
             }
 
-            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)
             {
@@ -1209,15 +1195,11 @@ namespace System.Collections.Generic
                 }
             }
 
-            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)
             {
index 57f3ed2..3282996 100644 (file)
@@ -591,7 +591,7 @@ namespace System.Collections.Generic
 
         IEnumerator<T> IEnumerable<T>.GetEnumerator() => GetEnumerator();
 
-        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+        IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable<T>)this).GetEnumerator();
 
         #endregion
 
index ae5cc7b..2bc209c 100644 (file)
@@ -152,21 +152,14 @@ namespace System.Collections.Generic
         }
 
         // 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()
         {
index f438689..a71dad9 100644 (file)
@@ -9,6 +9,8 @@ namespace System.Collections.Tests
 {
     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;
@@ -61,6 +63,7 @@ namespace System.Collections.Tests
         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;
index 936705f..efb7ef3 100644 (file)
@@ -13,6 +13,9 @@ namespace System.Collections.Tests
         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()
         {
@@ -62,10 +65,12 @@ namespace System.Collections.Tests
         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);
 
index b42b173..fa12ef5 100644 (file)
@@ -14,6 +14,10 @@ namespace System.Collections.Tests
     /// </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;
index 31271dc..1a0845a 100644 (file)
@@ -12,6 +12,9 @@ namespace System.Collections.Tests
 {
     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>();
index ff8901a..7067172 100644 (file)
@@ -16,6 +16,7 @@ namespace System.Collections.Tests
             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);
index 65261bf..c6aee13 100644 (file)
@@ -15,6 +15,8 @@ namespace System.Collections.Tests
     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;
 
index 1b4cf93..a3854d7 100644 (file)
@@ -8,6 +8,9 @@ namespace System.Collections.Tests
 {
     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)
index 9d656a8..7f0998c 100644 (file)
@@ -12,6 +12,9 @@ namespace System.Collections.Tests
     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()
         {
index 079acc7..e10c12c 100644 (file)
@@ -12,6 +12,9 @@ namespace System.Collections.Tests
     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()
         {
index d38d986..3fd0690 100644 (file)
@@ -50,8 +50,9 @@ namespace System.Collections.Tests
         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
index 8f8afc3..62b855d 100644 (file)
@@ -10,6 +10,8 @@ namespace System.Collections.Tests
     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);
 
index 55d7e44..db626df 100644 (file)
@@ -9,6 +9,8 @@ namespace System.Collections.Tests
 {
     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;
@@ -59,6 +61,7 @@ namespace System.Collections.Tests
         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()
index 3030fda..fd4669f 100644 (file)
@@ -9,6 +9,8 @@ namespace System.Collections.Tests
 {
     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;
@@ -61,6 +63,7 @@ namespace System.Collections.Tests
         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;
index cac00bc..8301811 100644 (file)
@@ -13,7 +13,9 @@ namespace System.Collections.Tests
     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()
index f83ed75..7b39393 100644 (file)
@@ -9,6 +9,8 @@ namespace System.Collections.Tests
     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()
         {
index 58e6938..4c5971c 100644 (file)
@@ -9,6 +9,9 @@ namespace System.Collections.Tests
 {
     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;
@@ -57,6 +60,7 @@ namespace System.Collections.Tests
         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;
 
index 14eeaa0..32ec7e6 100644 (file)
@@ -9,6 +9,9 @@ namespace System.Collections.Tests
 {
     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;
@@ -57,6 +60,7 @@ namespace System.Collections.Tests
         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;
 
index 7fc503b..2a7966d 100644 (file)
@@ -13,6 +13,9 @@ namespace System.Collections.Tests
     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()
         {
index 85bec3a..d0c7435 100644 (file)
@@ -9,7 +9,8 @@ namespace System.Collections.Tests
     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>();
index 1edc037..4c5c5a4 100644 (file)
@@ -32,6 +32,9 @@ namespace System.Collections.Tests
 
         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()
index 76a6e74..05a4cb3 100644 (file)
@@ -23,7 +23,9 @@ namespace System.Collections.Tests
             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);
 
index a5431a9..45fe8fd 100644 (file)
@@ -261,12 +261,18 @@ namespace System
 
         #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()
index 3b840d0..f959827 100644 (file)
@@ -339,7 +339,8 @@ namespace System.Collections.Generic
         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)
         {
@@ -1100,7 +1101,7 @@ namespace System.Collections.Generic
             }
         }
 
-        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
@@ -1513,9 +1514,11 @@ namespace System.Collections.Generic
                 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)
             {
@@ -1705,9 +1708,11 @@ namespace System.Collections.Generic
 
             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)
             {
index fe7912a..7537e2d 100644 (file)
@@ -358,9 +358,11 @@ namespace System.Collections.Generic
 
         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
 
index 1cfe6a6..b3c1655 100644 (file)
@@ -625,14 +625,13 @@ namespace System.Collections.Generic
         // 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)
         {
@@ -1145,6 +1144,8 @@ namespace System.Collections.Generic
 
         public struct Enumerator : IEnumerator<T>, IEnumerator
         {
+            internal static IEnumerator<T>? s_emptyEnumerator;
+
             private readonly List<T> _list;
             private int _index;
             private readonly int _version;
index 98a2f20..ba27a5e 100644 (file)
@@ -183,21 +183,14 @@ namespace System.Collections.Generic
 
         // 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
index 57be381..874db5f 100644 (file)
@@ -43,9 +43,8 @@ namespace System.Collections.ObjectModel
         }
 
         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)
         {
index 0c9806e..7a8f8da 100644 (file)
@@ -258,7 +258,7 @@ namespace System.Runtime.CompilerServices
             {
                 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);
             }
         }
index bbe9a86..171b9bf 100644 (file)
@@ -24,6 +24,7 @@ namespace System.Tests
             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;