| File: Language\TagHelperBinder.TagHelperSet.cs | Web Access |
| Project: src\roslyn\src\Razor\src\Compiler\Microsoft.CodeAnalysis.Razor.Compiler\src\Microsoft.CodeAnalysis.Razor.Compiler.csproj (Microsoft.CodeAnalysis.Razor.Compiler) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Diagnostics; namespace Microsoft.AspNetCore.Razor.Language; internal sealed partial class TagHelperBinder { /// <summary> /// Similar to <see cref="ImmutableArray{T}"/>, but optimized to store either a single value or an array of values. /// </summary> [DebuggerDisplay("{GetDebuggerDisplay(),nq}")] [DebuggerTypeProxy(typeof(DebuggerProxy))] private readonly struct TagHelperSet { public static readonly TagHelperSet Empty = default!; private readonly object _valueOrArray; public TagHelperSet(TagHelperDescriptor value) { _valueOrArray = value; } public TagHelperSet(TagHelperDescriptor[] array) { _valueOrArray = array; } public TagHelperDescriptor this[int index] { get { return _valueOrArray switch { TagHelperDescriptor[] array => array[index], not null when index == 0 => (TagHelperDescriptor)_valueOrArray, _ => throw new IndexOutOfRangeException(), }; } } public int Count => _valueOrArray switch { TagHelperDescriptor[] array => array.Length, null => 0, // _valueOrArray can be an array, a single value, or null. // So, we can avoid a type check for the single value case. _ => 1 }; public Enumerator GetEnumerator() => new(this); public struct Enumerator { private readonly TagHelperSet _tagHelperSet; private int _index; internal Enumerator(TagHelperSet tagHelperSet) { _tagHelperSet = tagHelperSet; _index = -1; } public bool MoveNext() { _index++; return _index < _tagHelperSet.Count; } public readonly TagHelperDescriptor Current => _tagHelperSet[_index]; } private sealed class DebuggerProxy(TagHelperSet instance) { private readonly TagHelperSet _instance = instance; [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] public TagHelperDescriptor[] Items => _instance._valueOrArray switch { TagHelperDescriptor[] array => array, TagHelperDescriptor value => [value], _ => [] }; } private string GetDebuggerDisplay() => "Count " + Count; /// <summary> /// This is a mutable builder for <see cref="TagHelperSet"/>. However, it works differently from /// a typical builder. First, you must call <see cref="IncreaseSize"/> to set the number of items. /// Once you've done that for each item to be added, you can call <see cref="Add(TagHelperDescriptor)"/> /// exactly that many times. This ensures that space allocated is exactly what's needed to /// produce the resulting <see cref="TagHelperSet"/>. /// </summary> public struct Builder { private object? _valueOrArray; private int _index; private int _size; public void IncreaseSize() { Debug.Assert(_valueOrArray is null, "Cannot increase size once items have been added."); _size++; } public void Add(TagHelperDescriptor item) { Debug.Assert(_index < _size, "Cannot add more items."); if (_size == 1) { // We only need to store a single value. _valueOrArray = item; _index = 1; return; } Debug.Assert(_valueOrArray is null or TagHelperDescriptor[]); if (_valueOrArray is not TagHelperDescriptor[] array) { array = new TagHelperDescriptor[_size]; _valueOrArray = array; } array[_index++] = item; } public readonly TagHelperSet ToSet() { Debug.Assert(_index == _size, "Must have added all items."); return _size switch { 0 => Empty, 1 => new((TagHelperDescriptor)_valueOrArray!), _ => new((TagHelperDescriptor[])_valueOrArray!) }; } } } }