diff --git a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/ReadOnlyTagHelperAttributeList.cs b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/ReadOnlyTagHelperAttributeList.cs index 19694da295..714805643d 100644 --- a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/ReadOnlyTagHelperAttributeList.cs +++ b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/ReadOnlyTagHelperAttributeList.cs @@ -5,7 +5,6 @@ using System; using System.Collections; using System.Collections.Generic; using System.Linq; -using Microsoft.Extensions.Internal; namespace Microsoft.AspNet.Razor.TagHelpers { @@ -50,13 +49,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers protected List Attributes { get; } /// - public TAttribute this[int index] - { - get - { - return Attributes[index]; - } - } + public TAttribute this[int index] => Attributes[index]; /// /// Gets the first with @@ -78,18 +71,21 @@ namespace Microsoft.AspNet.Razor.TagHelpers throw new ArgumentNullException(nameof(name)); } - return Attributes.FirstOrDefault(attribute => NameEquals(name, attribute)); + // Perf: Avoid allocating enumerator + for (var i = 0; i < Attributes.Count; i++) + { + if (NameEquals(name, Attributes[i])) + { + return Attributes[i]; + } + } + + return default(TAttribute); } } /// - public int Count - { - get - { - return Attributes.Count; - } - } + public int Count => Attributes.Count; /// /// Determines whether a matching exists in the @@ -131,7 +127,16 @@ namespace Microsoft.AspNet.Razor.TagHelpers throw new ArgumentNullException(nameof(name)); } - return Attributes.Any(attribute => NameEquals(name, attribute)); + // Perf: Avoid allocating enumerator + for (var i = 0; i < Attributes.Count; i++) + { + if (NameEquals(name, Attributes[i])) + { + return true; + } + } + + return false; } /// @@ -173,7 +178,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers throw new ArgumentNullException(nameof(name)); } - attribute = Attributes.FirstOrDefault(attr => NameEquals(name, attr)); + attribute = this[name]; return attribute != null; } @@ -197,9 +202,23 @@ namespace Microsoft.AspNet.Razor.TagHelpers throw new ArgumentNullException(nameof(name)); } - attributes = Attributes.Where(attribute => NameEquals(name, attribute)); + // Perf: Avoid allocating enumerator + List matchedAttributes = null; + for (var i = 0; i < Attributes.Count; i++) + { + if (NameEquals(name, Attributes[i])) + { + if (matchedAttributes == null) + { + matchedAttributes = new List(); + } - return attributes.Any(); + matchedAttributes.Add(Attributes[i]); + } + } + attributes = matchedAttributes ?? Enumerable.Empty(); + + return matchedAttributes != null; } /// diff --git a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperAttributeList.cs b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperAttributeList.cs index a98971701d..f6cd6807e7 100644 --- a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperAttributeList.cs +++ b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperAttributeList.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using Microsoft.AspNet.Razor.Runtime; -using Microsoft.Extensions.Internal; namespace Microsoft.AspNet.Razor.TagHelpers { @@ -134,6 +133,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers var attributeReplaced = false; + // Perf: Avoid allocating enumerator for (var i = 0; i < Attributes.Count; i++) { if (NameEquals(name, Attributes[i])) @@ -280,7 +280,18 @@ namespace Microsoft.AspNet.Razor.TagHelpers throw new ArgumentNullException(nameof(name)); } - return Attributes.RemoveAll(attribute => NameEquals(name, attribute)) > 0; + // Perf: Avoid allocating enumerator + var removedAtLeastOne = false; + for (var i = Attributes.Count - 1; i >= 0; i--) + { + if (NameEquals(name, Attributes[i])) + { + Attributes.RemoveAt(i); + removedAtLeastOne = true; + } + } + + return removedAtLeastOne; } /// diff --git a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperContext.cs b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperContext.cs index 272ec5a48c..76faafd8bc 100644 --- a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperContext.cs +++ b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperContext.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Linq; namespace Microsoft.AspNet.Razor.TagHelpers { @@ -12,6 +11,9 @@ namespace Microsoft.AspNet.Razor.TagHelpers /// public class TagHelperContext { + private ReadOnlyTagHelperAttributeList _allAttributes; + private IEnumerable _allAttributesData; + /// /// Instantiates a new . /// @@ -39,8 +41,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers throw new ArgumentNullException(nameof(uniqueId)); } - AllAttributes = new ReadOnlyTagHelperAttributeList( - allAttributes.Select(attribute => new TagHelperAttribute(attribute.Name, attribute.Value))); + _allAttributesData = allAttributes; Items = items; UniqueId = uniqueId; } @@ -48,7 +49,18 @@ namespace Microsoft.AspNet.Razor.TagHelpers /// /// Every attribute associated with the current HTML element. /// - public ReadOnlyTagHelperAttributeList AllAttributes { get; } + public ReadOnlyTagHelperAttributeList AllAttributes + { + get + { + if (_allAttributes == null) + { + _allAttributes = new ReadOnlyTagHelperAttributeList(_allAttributesData); + } + + return _allAttributes; + } + } /// /// Gets the collection of items used to communicate with other s. diff --git a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperOutput.cs b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperOutput.cs index e288bc4c9a..95438fe6f5 100644 --- a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperOutput.cs +++ b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperOutput.cs @@ -54,7 +54,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers } TagName = tagName; - Attributes = new TagHelperAttributeList(attributes); + Attributes = attributes; _getChildContentAsync = getChildContentAsync; } @@ -243,8 +243,10 @@ namespace Microsoft.AspNet.Razor.TagHelpers writer.Write('<'); writer.Write(TagName); - foreach (var attribute in Attributes) + // Perf: Avoid allocating enumerator + for (var i = 0; i < Attributes.Count; i++) { + var attribute = Attributes[i]; writer.Write(' '); writer.Write(attribute.Name);