Remove array enumerator allocations (#49970)
authorDavid Fowler <davidfowl@gmail.com>
Mon, 22 Mar 2021 06:17:27 +0000 (23:17 -0700)
committerGitHub <noreply@github.com>
Mon, 22 Mar 2021 06:17:27 +0000 (23:17 -0700)
- The default DI container uses arrays under the covers for IEnumerable<T> so we can take advantage of that to avoid allocating an array when storing a list of services and when enumerating them (when happens more often for OptionsFactory).
- If the IEnumerable<T> isn't an array, we will convert it into one, paying that cost once instead of allocating an enumerator on calls to Create.
- Also removed the IOptionsChangeTokenSource<TOptions> field from OptionsMonitor since it isn't used more than once.

src/libraries/Microsoft.Extensions.Options/src/OptionsFactory.cs
src/libraries/Microsoft.Extensions.Options/src/OptionsMonitor.cs

index 35c383d..f2dcad2 100644 (file)
@@ -4,6 +4,7 @@
 using System;
 using System.Collections.Generic;
 using System.Diagnostics.CodeAnalysis;
+using System.Linq;
 
 namespace Microsoft.Extensions.Options
 {
@@ -15,9 +16,9 @@ namespace Microsoft.Extensions.Options
         IOptionsFactory<TOptions>
         where TOptions : class
     {
-        private readonly IEnumerable<IConfigureOptions<TOptions>> _setups;
-        private readonly IEnumerable<IPostConfigureOptions<TOptions>> _postConfigures;
-        private readonly IEnumerable<IValidateOptions<TOptions>> _validations;
+        private readonly IConfigureOptions<TOptions>[] _setups;
+        private readonly IPostConfigureOptions<TOptions>[] _postConfigures;
+        private readonly IValidateOptions<TOptions>[] _validations;
 
         /// <summary>
         /// Initializes a new instance with the specified options configurations.
@@ -35,9 +36,9 @@ namespace Microsoft.Extensions.Options
         /// <param name="validations">The validations to run.</param>
         public OptionsFactory(IEnumerable<IConfigureOptions<TOptions>> setups, IEnumerable<IPostConfigureOptions<TOptions>> postConfigures, IEnumerable<IValidateOptions<TOptions>> validations)
         {
-            _setups = setups;
-            _postConfigures = postConfigures;
-            _validations = validations;
+            _setups = setups as IConfigureOptions<TOptions>[] ?? setups.ToArray();
+            _postConfigures = postConfigures as IPostConfigureOptions<TOptions>[] ?? postConfigures.ToArray();
+            _validations = validations as IValidateOptions<TOptions>[] ?? validations.ToArray();
         }
 
         /// <summary>
index 7c43e28..47ec6ab 100644 (file)
@@ -4,6 +4,7 @@
 using System;
 using System.Collections.Generic;
 using System.Diagnostics.CodeAnalysis;
+using System.Linq;
 using Microsoft.Extensions.Primitives;
 
 namespace Microsoft.Extensions.Options
@@ -19,7 +20,6 @@ namespace Microsoft.Extensions.Options
     {
         private readonly IOptionsMonitorCache<TOptions> _cache;
         private readonly IOptionsFactory<TOptions> _factory;
-        private readonly IEnumerable<IOptionsChangeTokenSource<TOptions>> _sources;
         private readonly List<IDisposable> _registrations = new List<IDisposable>();
         internal event Action<TOptions, string> _onChange;
 
@@ -32,10 +32,9 @@ namespace Microsoft.Extensions.Options
         public OptionsMonitor(IOptionsFactory<TOptions> factory, IEnumerable<IOptionsChangeTokenSource<TOptions>> sources, IOptionsMonitorCache<TOptions> cache)
         {
             _factory = factory;
-            _sources = sources;
             _cache = cache;
 
-            foreach (IOptionsChangeTokenSource<TOptions> source in _sources)
+            foreach (IOptionsChangeTokenSource<TOptions> source in (sources as IOptionsChangeTokenSource<TOptions>[] ?? sources.ToArray()))
             {
                 IDisposable registration = ChangeToken.OnChange(
                       () => source.GetChangeToken(),