diff --git a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs index 8a32e8f646d1a..699f99c441fdf 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs @@ -400,7 +400,8 @@ internal IEnumerator GetEnumerator() // ! Warning: "this" is an array, not an SZArrayHelper. See comments above // ! or you may introduce a security hole! T[] @this = Unsafe.As(this); - return @this.Length == 0 ? SZGenericArrayEnumerator.Empty : new SZGenericArrayEnumerator(@this); + int length = @this.Length; + return length == 0 ? SZGenericArrayEnumerator.Empty : new SZGenericArrayEnumerator(@this, length); } private void CopyTo(T[] array, int index) diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Array.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Array.NativeAot.cs index f2d1aac03130b..1cb5604cefbd5 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Array.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Array.NativeAot.cs @@ -1099,7 +1099,10 @@ private Array() { } public new IEnumerator GetEnumerator() { T[] @this = Unsafe.As(this); - return @this.Length == 0 ? SZGenericArrayEnumerator.Empty : new SZGenericArrayEnumerator(@this); + // get length so we don't have to call the Length property again in ArrayEnumerator constructor + // and avoid more checking there too. + int length = @this.Length; + return length == 0 ? SZGenericArrayEnumerator.Empty : new SZGenericArrayEnumerator(@this, length); } public int Count diff --git a/src/libraries/System.Private.CoreLib/src/System/Array.Enumerators.cs b/src/libraries/System.Private.CoreLib/src/System/Array.Enumerators.cs index 90542e9685cf5..60aa83909d4b6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Array.Enumerators.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Array.Enumerators.cs @@ -69,72 +69,60 @@ public void Reset() internal abstract class SZGenericArrayEnumeratorBase : IDisposable { - protected readonly Array _array; protected int _index; + protected readonly int _endIndex; - protected SZGenericArrayEnumeratorBase(Array array) + protected SZGenericArrayEnumeratorBase(int endIndex) { - Debug.Assert(array != null); - - _array = array; _index = -1; + _endIndex = endIndex; } public bool MoveNext() { int index = _index + 1; - uint length = (uint)_array.NativeLength; - if ((uint)index >= length) + if ((uint)index < (uint)_endIndex) { - _index = (int)length; - return false; + _index = index; + return true; } - _index = index; - return true; + _index = _endIndex; + return false; } public void Reset() => _index = -1; -#pragma warning disable CA1822 // https://github.com/dotnet/roslyn-analyzers/issues/5911 public void Dispose() { } -#pragma warning restore CA1822 } internal sealed class SZGenericArrayEnumerator : SZGenericArrayEnumeratorBase, IEnumerator { + private readonly T[]? _array; + /// Provides an empty enumerator singleton. /// /// If the consumer is using SZGenericArrayEnumerator elsewhere or is otherwise likely /// to be using T[] elsewhere, this singleton should be used. Otherwise, GenericEmptyEnumerator's /// singleton should be used instead, as it doesn't reference T[] in order to reduce footprint. /// -#pragma warning disable CA1825 - internal static readonly SZGenericArrayEnumerator Empty = - // Array.Empty is intentionally omitted here, since we don't want to pay for generic instantiations - // that wouldn't have otherwise been used. - new SZGenericArrayEnumerator(new T[0]); -#pragma warning restore CA1825 - - public SZGenericArrayEnumerator(T[] array) - : base(array) + internal static readonly SZGenericArrayEnumerator Empty = new SZGenericArrayEnumerator(null, 0); + + internal SZGenericArrayEnumerator(T[]? array, int endIndex) + : base(endIndex) { + Debug.Assert(array == null || endIndex == array.Length); + _array = array; } public T Current { get { - int index = _index; - T[] array = Unsafe.As(_array); - - if ((uint)index >= (uint)array.Length) - { - ThrowHelper.ThrowInvalidOperationException_EnumCurrent(index); - } - - return array[index]; + if ((uint)_index >= (uint)_endIndex) + ThrowHelper.ThrowInvalidOperationException_EnumCurrent(_index); + return _array![_index]; } } diff --git a/src/mono/System.Private.CoreLib/src/System/Array.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Array.Mono.cs index af3e7f05fc1ab..f6782a42a23be 100644 --- a/src/mono/System.Private.CoreLib/src/System/Array.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Array.Mono.cs @@ -468,7 +468,8 @@ internal bool InternalArray__ICollection_get_IsReadOnly() internal IEnumerator InternalArray__IEnumerable_GetEnumerator() { - return Length == 0 ? SZGenericArrayEnumerator.Empty : new SZGenericArrayEnumerator(Unsafe.As(this)); + int length = Length; + return length == 0 ? SZGenericArrayEnumerator.Empty : new SZGenericArrayEnumerator(Unsafe.As(this), length); } internal void InternalArray__ICollection_Clear()