Right-size Lists when created (#23714)

Create new instances of List<T> with an appropriate capacity for the items that will be added.
Use Array.Empty<T>() where appropriate, rather than create an empty list and then return it.
This commit is contained in:
Martin Costello 2020-08-19 17:45:52 +01:00 committed by GitHub
parent d2f34d6d99
commit b22512de0e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 71 additions and 66 deletions

View File

@ -98,7 +98,7 @@ namespace Microsoft.Extensions.FileProviders.Embedded.Manifest
private ManifestEntry[] CopyChildren()
{
var list = new List<ManifestEntry>();
var list = new List<ManifestEntry>(Children.Count);
for (int i = 0; i < Children.Count; i++)
{
var child = Children[i];

View File

@ -80,7 +80,7 @@ namespace Microsoft.AspNetCore.Http.Features
}
else
{
var headers = new List<string>();
var headers = new List<string>(_parsedValues.Count);
foreach (var pair in _parsedValues)
{
headers.Add(new CookieHeaderValue(pair.Key, pair.Value).ToString());

View File

@ -497,7 +497,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
updatedParameterPolicies = new Dictionary<string, List<RoutePatternParameterPolicyReference>>(StringComparer.OrdinalIgnoreCase);
}
parameterConstraints = new List<RoutePatternParameterPolicyReference>();
parameterConstraints = new List<RoutePatternParameterPolicyReference>(parameter.ParameterPolicies.Count);
updatedParameterPolicies.Add(parameter.Name, parameterConstraints);
}

View File

@ -169,16 +169,22 @@ namespace Microsoft.AspNetCore.Identity
public virtual async Task RefreshSignInAsync(TUser user)
{
var auth = await Context.AuthenticateAsync(IdentityConstants.ApplicationScheme);
var claims = new List<Claim>();
IList<Claim> claims = Array.Empty<Claim>();
var authenticationMethod = auth?.Principal?.FindFirst(ClaimTypes.AuthenticationMethod);
if (authenticationMethod != null)
{
claims.Add(authenticationMethod);
}
var amr = auth?.Principal?.FindFirst("amr");
if (amr != null)
if (authenticationMethod != null || amr != null)
{
claims.Add(amr);
claims = new List<Claim>();
if (authenticationMethod != null)
{
claims.Add(authenticationMethod);
}
if (amr != null)
{
claims.Add(amr);
}
}
await SignInWithClaimsAsync(user, auth?.Properties, claims);
@ -203,9 +209,10 @@ namespace Microsoft.AspNetCore.Identity
/// <returns>The task object representing the asynchronous operation.</returns>
public virtual Task SignInAsync(TUser user, AuthenticationProperties authenticationProperties, string authenticationMethod = null)
{
var additionalClaims = new List<Claim>();
IList<Claim> additionalClaims = Array.Empty<Claim>();
if (authenticationMethod != null)
{
additionalClaims = new List<Claim>();
additionalClaims.Add(new Claim(ClaimTypes.AuthenticationMethod, authenticationMethod));
}
return SignInWithClaimsAsync(user, authenticationProperties, additionalClaims);

View File

@ -122,7 +122,7 @@ namespace Microsoft.AspNetCore.Builder
/// <returns>The <see cref="RequestLocalizationOptions"/>.</returns>
public RequestLocalizationOptions AddSupportedCultures(params string[] cultures)
{
var supportedCultures = new List<CultureInfo>();
var supportedCultures = new List<CultureInfo>(cultures.Length);
foreach (var culture in cultures)
{
@ -140,7 +140,7 @@ namespace Microsoft.AspNetCore.Builder
/// <returns>The <see cref="RequestLocalizationOptions"/>.</returns>
public RequestLocalizationOptions AddSupportedUICultures(params string[] uiCultures)
{
var supportedUICultures = new List<CultureInfo>();
var supportedUICultures = new List<CultureInfo>(uiCultures.Length);
foreach (var culture in uiCultures)
{
supportedUICultures.Add(new CultureInfo(culture));

View File

@ -309,7 +309,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
// Text to show as the attribute route template for conventionally routed actions.
var nullTemplate = Resources.AttributeRoute_NullTemplateRepresentation;
var actionDescriptions = new List<string>();
var actionDescriptions = new List<string>(actions.Count);
for (var i = 0; i < actions.Count; i++)
{
var (action, selector) = actions[i];

View File

@ -63,7 +63,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
private static void AddParameterDescriptors(ActionDescriptor actionDescriptor, ActionModel action)
{
var parameterDescriptors = new List<ParameterDescriptor>();
var parameterDescriptors = new List<ParameterDescriptor>(action.Parameters.Count);
foreach (var parameter in action.Parameters)
{
var parameterDescriptor = CreateParameterDescriptor(parameter);

View File

@ -352,7 +352,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
// This is fairly complicated so that we maintain referential equality between items in
// ActionModel.Attributes and ActionModel.Attributes[*].Attribute.
var applicableAttributes = new List<object>();
var applicableAttributes = new List<object>(routeAttributes.Length);
foreach (var attribute in attributes)
{
if (attribute is IRouteTemplateProvider)

View File

@ -25,7 +25,7 @@ namespace Microsoft.AspNetCore.Mvc
/// <param name="include">Names of parameters to include in binding.</param>
public BindAttribute(params string[] include)
{
var items = new List<string>();
var items = new List<string>(include.Length);
foreach (var item in include)
{
items.AddRange(SplitString(item));

View File

@ -199,7 +199,7 @@ namespace Microsoft.AspNetCore.Mvc
private MediaTypeCollection GetContentTypes(string firstArg, string[] args)
{
var completeArgs = new List<string>();
var completeArgs = new List<string>(args.Length + 1);
completeArgs.Add(firstArg);
completeArgs.AddRange(args);
var contentTypes = new MediaTypeCollection();

View File

@ -159,7 +159,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
{
if (mediaTypes == null)
{
mediaTypes = new List<string>();
mediaTypes = new List<string>(SupportedMediaTypes.Count);
}
mediaTypes.Add(mediaType);

View File

@ -67,7 +67,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
{
if (mediaTypes == null)
{
mediaTypes = new List<string>();
mediaTypes = new List<string>(SupportedMediaTypes.Count);
}
mediaTypes.Add(contentType);
@ -81,7 +81,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
{
if (mediaTypes == null)
{
mediaTypes = new List<string>();
mediaTypes = new List<string>(SupportedMediaTypes.Count);
}
mediaTypes.Add(mediaType);

View File

@ -135,10 +135,11 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
RouteContext context,
IReadOnlyList<ActionDescriptor> actions)
{
var candidates = new List<ActionSelectorCandidate>();
var actionsCount = actions.Count;
var candidates = new List<ActionSelectorCandidate>(actionsCount);
// Perf: Avoid allocations
for (var i = 0; i < actions.Count; i++)
for (var i = 0; i < actionsCount; i++)
{
var action = actions[i];
var constraints = _actionConstraintCache.GetActionConstraints(context.HttpContext, action);
@ -150,9 +151,10 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
List<ActionDescriptor> results = null;
if (matches != null)
{
results = new List<ActionDescriptor>(matches.Count);
var matchesCount = matches.Count;
results = new List<ActionDescriptor>(matchesCount);
// Perf: Avoid allocations
for (var i = 0; i < matches.Count; i++)
for (var i = 0; i < matchesCount; i++)
{
var candidate = matches[i];
results.Add(candidate.Action);

View File

@ -111,7 +111,7 @@ namespace Microsoft.AspNetCore.Mvc
private MediaTypeCollection GetContentTypes(string firstArg, string[] args)
{
var completeArgs = new List<string>();
var completeArgs = new List<string>(args.Length + 1);
completeArgs.Add(firstArg);
completeArgs.AddRange(args);
var contentTypes = new MediaTypeCollection();

View File

@ -52,7 +52,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation
// For unit testing
internal IEnumerable<string> GetReferencePaths()
{
var referencePaths = new List<string>();
var referencePaths = new List<string>(_options.AdditionalReferencePaths.Count);
foreach (var part in _partManager.ApplicationParts)
{

View File

@ -83,7 +83,8 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Infrastructure
private static object DeserializeArray(in JsonElement arrayElement)
{
if (arrayElement.GetArrayLength() == 0)
int arrayLength = arrayElement.GetArrayLength();
if (arrayLength == 0)
{
// We have to infer the type of the array by inspecting it's elements.
// If there's nothing to inspect, return a null value since we do not know
@ -93,7 +94,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Infrastructure
if (arrayElement[0].ValueKind == JsonValueKind.String)
{
var array = new List<string>();
var array = new List<string>(arrayLength);
foreach (var item in arrayElement.EnumerateArray())
{
@ -104,7 +105,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Infrastructure
}
else if (arrayElement[0].ValueKind == JsonValueKind.Number)
{
var array = new List<int>();
var array = new List<int>(arrayLength);
foreach (var item in arrayElement.EnumerateArray())
{

View File

@ -170,7 +170,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
// This method is overloaded on string and T, which means that it will put the code in the
// correct context for intellisense when typing in the attribute.
var eventArgsType = node.TagHelper.GetEventArgsType();
var tokens = new List<IntermediateToken>()
var tokens = new List<IntermediateToken>(original.Count + 2)
{
new IntermediateToken()
{

View File

@ -96,7 +96,7 @@ namespace Microsoft.AspNetCore.Razor.Tools
private static string[] ExpandResponseFiles(string[] args)
{
var expandedArgs = new List<string>();
var expandedArgs = new List<string>(args.Length);
foreach (var arg in args)
{
if (!arg.StartsWith("@", StringComparison.Ordinal))

View File

@ -143,7 +143,7 @@ namespace Microsoft.AspNetCore.Razor.Tools
/// </summary>
private void WaitForAnyCompletion(CancellationToken cancellationToken)
{
var all = new List<Task>();
var all = new List<Task>(_connections.Count + 3);
all.AddRange(_connections);
all.Add(_timeoutTask);
all.Add(_listenTask);

View File

@ -33,7 +33,7 @@ namespace Microsoft.AspNetCore.Razor.Tools
internal static AssemblyIdentity[] GetReferencedAssembliesOrThrow(this MetadataReader reader)
{
var references = new List<AssemblyIdentity>();
var references = new List<AssemblyIdentity>(reader.AssemblyReferences.Count);
foreach (var referenceHandle in reader.AssemblyReferences)
{

View File

@ -23,7 +23,7 @@ namespace Microsoft.AspNetCore.Razor.Tasks
public override bool Execute()
{
var referenceItems = new List<AssemblyItem>();
var referenceItems = new List<AssemblyItem>(Assemblies.Length);
foreach (var item in Assemblies)
{
const string FusionNameKey = "FusionName";

View File

@ -120,7 +120,7 @@ namespace Microsoft.AspNetCore.Razor.Tasks
var metadataReader = peReader.GetMetadataReader();
var references = new List<AssemblyItem>();
var references = new List<AssemblyItem>(metadataReader.AssemblyReferences.Count);
foreach (var handle in metadataReader.AssemblyReferences)
{
var reference = metadataReader.GetAssemblyReference(handle);

View File

@ -12,8 +12,6 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
/// </summary>
public abstract class ReadOnlyTagHelperAttributeList : ReadOnlyCollection<TagHelperAttribute>
{
private static readonly IReadOnlyList<TagHelperAttribute> EmptyList = new TagHelperAttribute[0];
/// <summary>
/// Instantiates a new instance of <see cref="ReadOnlyTagHelperAttributeList"/> with an empty
/// collection.
@ -146,7 +144,7 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
matchedAttributes.Add(attribute);
}
}
attributes = matchedAttributes ?? EmptyList;
attributes = matchedAttributes ?? (IReadOnlyList<TagHelperAttribute>)Array.Empty<TagHelperAttribute>();
return matchedAttributes != null;
}
@ -198,4 +196,4 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
return string.Equals(name, attribute.Name, StringComparison.OrdinalIgnoreCase);
}
}
}
}

View File

@ -12,8 +12,6 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
/// </summary>
public abstract class ReadOnlyTagHelperAttributeList : ReadOnlyCollection<TagHelperAttribute>
{
private static readonly IReadOnlyList<TagHelperAttribute> EmptyList = new TagHelperAttribute[0];
/// <summary>
/// Instantiates a new instance of <see cref="ReadOnlyTagHelperAttributeList"/> with an empty
/// collection.
@ -138,7 +136,7 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
matchedAttributes.Add(Items[i]);
}
}
attributes = matchedAttributes ?? EmptyList;
attributes = matchedAttributes ?? Array.Empty<TagHelperAttribute>() as IReadOnlyList<TagHelperAttribute>;
return matchedAttributes != null;
}

View File

@ -131,7 +131,7 @@ namespace Microsoft.AspNetCore.Authentication.Certificate
var certificateIsValid = chain.Build(clientCertificate);
if (!certificateIsValid)
{
var chainErrors = new List<string>();
var chainErrors = new List<string>(chain.ChainStatus.Length);
foreach (var validationFailure in chain.ChainStatus)
{
chainErrors.Add($"{validationFailure.Status} {validationFailure.StatusInformation}");

View File

@ -274,7 +274,7 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
private static string CreateErrorDescription(Exception authFailure)
{
IEnumerable<Exception> exceptions;
IReadOnlyCollection<Exception> exceptions;
if (authFailure is AggregateException agEx)
{
exceptions = agEx.InnerExceptions;
@ -284,7 +284,7 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
exceptions = new[] { authFailure };
}
var messages = new List<string>();
var messages = new List<string>(exceptions.Count);
foreach (var ex in exceptions)
{

View File

@ -107,13 +107,13 @@ namespace Microsoft.AspNetCore.Server.HttpSys
internal static IList<string> GenerateChallenges(AuthenticationSchemes authSchemes)
{
IList<string> challenges = new List<string>();
if (authSchemes == AuthenticationSchemes.None)
{
return challenges;
return Array.Empty<string>();
}
IList<string> challenges = new List<string>();
// Order by strength.
if ((authSchemes & AuthenticationSchemes.Kerberos) == AuthenticationSchemes.Kerberos)
{

View File

@ -343,7 +343,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
// Copied from the multi-value headers section of SerializeHeaders
if (authChallenges != null && authChallenges.Count > 0)
{
pinnedHeaders = new List<GCHandle>();
pinnedHeaders = new List<GCHandle>(authChallenges.Count + 3);
HttpApiTypes.HTTP_RESPONSE_INFO[] knownHeaderInfo = null;
knownHeaderInfo = new HttpApiTypes.HTTP_RESPONSE_INFO[1];

View File

@ -92,7 +92,7 @@ namespace System.Buffers
public override ReadOnlySequence<byte> CreateWithContent(byte[] data)
{
var segments = new List<byte[]>();
var segments = new List<byte[]>((data.Length * 2) + 1);
segments.Add(Array.Empty<byte>());
foreach (var b in data)

View File

@ -157,8 +157,8 @@ namespace Microsoft.Extensions.Internal
var parametersParameter = Expression.Parameter(typeof(object[]), "parameters");
// Build parameter list
var parameters = new List<Expression>();
var paramInfos = methodInfo.GetParameters();
var parameters = new List<Expression>(paramInfos.Length);
for (int i = 0; i < paramInfos.Length; i++)
{
var paramInfo = paramInfos[i];
@ -209,8 +209,8 @@ namespace Microsoft.Extensions.Internal
var parametersParameter = Expression.Parameter(typeof(object[]), "parameters");
// Build parameter list
var parameters = new List<Expression>();
var paramInfos = methodInfo.GetParameters();
var parameters = new List<Expression>(paramInfos.Length);
for (int i = 0; i < paramInfos.Length; i++)
{
var paramInfo = paramInfos[i];

View File

@ -18,12 +18,10 @@ namespace Microsoft.Extensions.StackTrace.Sources
{
public static IList<StackFrameInfo> GetFrames(Exception exception, out AggregateException? error)
{
var frames = new List<StackFrameInfo>();
if (exception == null)
{
error = default;
return frames;
return Array.Empty<StackFrameInfo>();
}
var needFileInfo = true;
@ -33,9 +31,11 @@ namespace Microsoft.Extensions.StackTrace.Sources
if (stackFrames == null)
{
error = default;
return frames;
return Array.Empty<StackFrameInfo>();
}
var frames = new List<StackFrameInfo>(stackFrames.Length);
List<Exception>? exceptions = null;
for (var i = 0; i < stackFrames.Length; i++)

View File

@ -180,7 +180,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Internal
// Stop firing the timer
_nextHeartbeat.Stop();
var tasks = new List<Task>();
var tasks = new List<Task>(_connections.Count);
// REVIEW: In the future we can consider a hybrid where we first try to wait for shutdown
// for a certain time frame then after some grace period we shutdown more aggressively

View File

@ -73,8 +73,7 @@ namespace Microsoft.AspNetCore.SignalR
if (_hubOptions.HubFilters != null)
{
hubFilters = new List<IHubFilter>();
hubFilters.AddRange(_hubOptions.HubFilters);
hubFilters = new List<IHubFilter>(_hubOptions.HubFilters);
}
}
else
@ -84,8 +83,7 @@ namespace Microsoft.AspNetCore.SignalR
if (_globalHubOptions.HubFilters != null)
{
hubFilters = new List<IHubFilter>();
hubFilters.AddRange(_globalHubOptions.HubFilters);
hubFilters = new List<IHubFilter>(_globalHubOptions.HubFilters);
}
}

View File

@ -56,7 +56,7 @@ namespace Microsoft.AspNetCore.SignalR
if (options.SupportedProtocols == null)
{
options.SupportedProtocols = new List<string>();
options.SupportedProtocols = new List<string>(_defaultProtocols.Count);
}
if (options.StreamBufferCapacity == null)

View File

@ -10,11 +10,12 @@ namespace Microsoft.AspNetCore.SignalR.Internal
{
internal class DefaultHubMessageSerializer
{
private readonly List<IHubProtocol> _hubProtocols = new List<IHubProtocol>();
private readonly List<IHubProtocol> _hubProtocols;
public DefaultHubMessageSerializer(IHubProtocolResolver hubProtocolResolver, IList<string> globalSupportedProtocols, IList<string> hubSupportedProtocols)
{
var supportedProtocols = hubSupportedProtocols ?? globalSupportedProtocols ?? Array.Empty<string>();
_hubProtocols = new List<IHubProtocol>(supportedProtocols.Count);
foreach (var protocolName in supportedProtocols)
{
var protocol = hubProtocolResolver.GetProtocol(protocolName, (supportedProtocols as IReadOnlyList<string>) ?? supportedProtocols.ToList());

View File

@ -465,7 +465,7 @@ namespace Microsoft.AspNetCore.SignalR.StackExchangeRedis
{
var invocation = _protocol.ReadInvocation((byte[])channelMessage.Message);
var tasks = new List<Task>();
var tasks = new List<Task>(subscriptions.Count);
foreach (var userConnection in subscriptions)
{
tasks.Add(userConnection.WriteAsync(invocation.Message).AsTask());
@ -491,7 +491,7 @@ namespace Microsoft.AspNetCore.SignalR.StackExchangeRedis
{
var invocation = _protocol.ReadInvocation((byte[])channelMessage.Message);
var tasks = new List<Task>();
var tasks = new List<Task>(groupConnections.Count);
foreach (var groupConnection in groupConnections)
{
if (invocation.ExcludedConnectionIds?.Contains(groupConnection.ConnectionId) == true)