Remove System.Linq in Blazor WASM apps (#56937)
authorEric Erhardt <eric.erhardt@microsoft.com>
Fri, 6 Aug 2021 19:24:45 +0000 (14:24 -0500)
committerGitHub <noreply@github.com>
Fri, 6 Aug 2021 19:24:45 +0000 (15:24 -0400)
Removes the last usages of System.Linq in a default Blazor WASM app, which were a handful of ToArray calls.

Fix #56916

src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteFactory.cs
src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceProvider.cs
src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ServiceLookup/CallSiteFactoryTest.cs
src/libraries/Microsoft.Extensions.Options/src/OptionsFactory.cs
src/libraries/Microsoft.Extensions.Options/src/OptionsMonitor.cs

index 06bcdf6..02db795 100644 (file)
@@ -6,7 +6,6 @@ using System.Collections.Concurrent;
 using System.Collections.Generic;
 using System.Diagnostics;
 using System.Diagnostics.CodeAnalysis;
-using System.Linq;
 using System.Reflection;
 using Microsoft.Extensions.Internal;
 
@@ -22,10 +21,12 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
 
         private readonly StackGuard _stackGuard;
 
-        public CallSiteFactory(IEnumerable<ServiceDescriptor> descriptors)
+        public CallSiteFactory(ICollection<ServiceDescriptor> descriptors)
         {
             _stackGuard = new StackGuard();
-            _descriptors = descriptors.ToArray();
+            _descriptors = new ServiceDescriptor[descriptors.Count];
+            descriptors.CopyTo(_descriptors, 0);
+
             Populate();
         }
 
index cff40d1..9e3698f 100644 (file)
@@ -33,7 +33,7 @@ namespace Microsoft.Extensions.DependencyInjection
         internal static bool VerifyOpenGenericServiceTrimmability { get; } =
             AppContext.TryGetSwitch("Microsoft.Extensions.DependencyInjection.VerifyOpenGenericServiceTrimmability", out bool verifyOpenGenerics) ? verifyOpenGenerics : false;
 
-        internal ServiceProvider(IEnumerable<ServiceDescriptor> serviceDescriptors, ServiceProviderOptions options)
+        internal ServiceProvider(ICollection<ServiceDescriptor> serviceDescriptors, ServiceProviderOptions options)
         {
             // note that Root needs to be set before calling GetEngine(), because the engine may need to access Root
             Root = new ServiceProviderEngineScope(this, isRootScope: true);
index da24dca..2ca9d8b 100644 (file)
@@ -574,7 +574,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
                 ServiceDescriptor.Singleton<ICustomService, CustomService5>()
             };
 
-            var callsiteFactory = new CallSiteFactory(serviceDescriptors.Take(numberOfServices));
+            var callsiteFactory = new CallSiteFactory(serviceDescriptors.Take(numberOfServices).ToArray());
 
             for (int i = 0; i < numberOfServices; i++)
             {
index f14e2b1..60ca517 100644 (file)
@@ -4,7 +4,6 @@
 using System;
 using System.Collections.Generic;
 using System.Diagnostics.CodeAnalysis;
-using System.Linq;
 
 namespace Microsoft.Extensions.Options
 {
@@ -36,9 +35,14 @@ 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 as IConfigureOptions<TOptions>[] ?? setups.ToArray();
-            _postConfigures = postConfigures as IPostConfigureOptions<TOptions>[] ?? postConfigures.ToArray();
-            _validations = validations as IValidateOptions<TOptions>[] ?? validations.ToArray();
+            // The default DI container uses arrays under the covers. Take advantage of this knowledge
+            // by checking for an array and enumerate over that, so we don't need to allocate an enumerator.
+            // When it isn't already an array, convert it to one, but don't use System.Linq to avoid pulling Linq in to
+            // small trimmed applications.
+
+            _setups = setups as IConfigureOptions<TOptions>[] ?? new List<IConfigureOptions<TOptions>>(setups).ToArray();
+            _postConfigures = postConfigures as IPostConfigureOptions<TOptions>[] ?? new List<IPostConfigureOptions<TOptions>>(postConfigures).ToArray();
+            _validations = validations as IValidateOptions<TOptions>[] ?? new List<IValidateOptions<TOptions>>(validations).ToArray();
         }
 
         /// <summary>
index 06e5156..2b7fbf0 100644 (file)
@@ -4,7 +4,6 @@
 using System;
 using System.Collections.Generic;
 using System.Diagnostics.CodeAnalysis;
-using System.Linq;
 using Microsoft.Extensions.Primitives;
 
 namespace Microsoft.Extensions.Options
@@ -34,15 +33,32 @@ namespace Microsoft.Extensions.Options
             _factory = factory;
             _cache = cache;
 
-            foreach (IOptionsChangeTokenSource<TOptions> source in (sources as IOptionsChangeTokenSource<TOptions>[] ?? sources.ToArray()))
+            void RegisterSource(IOptionsChangeTokenSource<TOptions> source)
             {
                 IDisposable registration = ChangeToken.OnChange(
-                      () => source.GetChangeToken(),
-                      (name) => InvokeChanged(name),
-                      source.Name);
+                          () => source.GetChangeToken(),
+                          (name) => InvokeChanged(name),
+                          source.Name);
 
                 _registrations.Add(registration);
             }
+
+            // The default DI container uses arrays under the covers. Take advantage of this knowledge
+            // by checking for an array and enumerate over that, so we don't need to allocate an enumerator.
+            if (sources is IOptionsChangeTokenSource<TOptions>[] sourcesArray)
+            {
+                foreach (IOptionsChangeTokenSource<TOptions> source in sourcesArray)
+                {
+                    RegisterSource(source);
+                }
+            }
+            else
+            {
+                foreach (IOptionsChangeTokenSource<TOptions> source in sources)
+                {
+                    RegisterSource(source);
+                }
+            }
         }
 
         private void InvokeChanged(string name)