44using System ;
55using System . Collections . Generic ;
66using System . Collections . Immutable ;
7+ using System . ComponentModel ;
78using System . Diagnostics . CodeAnalysis ;
89using System . Runtime . CompilerServices ;
910using Microsoft . AspNetCore . Razor . Language . Intermediate ;
@@ -15,28 +16,28 @@ public sealed partial class RazorCodeDocument
1516 /// <summary>
1617 /// Represents a set of mutable values associated with a <see cref="RazorCodeDocument"/>.
1718 /// </summary>
18- private readonly struct PropertyTable
19+ private readonly struct PropertyTable ( )
1920 {
20- public const int Size = 10 ;
21-
22- private const int TagHelpersIndex = 0 ;
23- private const int ReferencedTagHelpersIndex = 1 ;
24- private const int PreTagHelperSyntaxTreeIndex = 2 ;
25- private const int SyntaxTreeIndex = 3 ;
26- private const int ImportSyntaxTreesIndex = 4 ;
27- private const int TagHelperContextIndex = 5 ;
28- private const int DocumentIntermediateNodeIndex = 6 ;
29- private const int CSharpDocumentIndex = 7 ;
30- private const int HtmlDocumentIndex = 8 ;
31- private const int NamespaceInfoIndex = 9 ;
32-
33- private readonly object ? [ ] _values ;
34-
35- public PropertyTable ( )
36- {
37- _values = new object ? [ Size ] ;
38- }
39-
21+ // To add a mutable value, increase Size by 1 and add a new property below.
22+ // Use a Property<T> for reference types or a BoxedProperty<T> for value types.
23+
24+ private const int Size = 10 ;
25+
26+ private readonly object ? [ ] _values = new object ? [ Size ] ;
27+
28+ public Property < IReadOnlyList < TagHelperDescriptor > > TagHelpers => new ( _values , 0 ) ;
29+ public Property < ISet < TagHelperDescriptor > > ReferencedTagHelpers => new ( _values , 1 ) ;
30+ public Property < RazorSyntaxTree > PreTagHelperSyntaxTree => new ( _values , 2 ) ;
31+ public Property < RazorSyntaxTree > SyntaxTree => new ( _values , 3 ) ;
32+ public BoxedProperty < ImmutableArray < RazorSyntaxTree > > ImportSyntaxTrees => new ( _values , 4 ) ;
33+ public Property < TagHelperDocumentContext > TagHelperContext => new ( _values , 5 ) ;
34+ public Property < DocumentIntermediateNode > DocumentNode => new ( _values , 6 ) ;
35+ public Property < RazorCSharpDocument > CSharpDocument => new ( _values , 7 ) ;
36+ public Property < RazorHtmlDocument > HtmlDocument => new ( _values , 8 ) ;
37+ public BoxedProperty < ( string name , SourceSpan ? span ) > NamespaceInfo => new ( _values , 9 ) ;
38+
39+ [ EditorBrowsable ( EditorBrowsableState . Never ) ]
40+ [ Obsolete ( "Do not use. Present to support the legacy editor" , error : false ) ]
4041 public PropertyTable Clone ( )
4142 {
4243 var clone = new PropertyTable ( ) ;
@@ -45,25 +46,36 @@ public PropertyTable Clone()
4546 return clone ;
4647 }
4748
48- public Property < IReadOnlyList < TagHelperDescriptor > > TagHelpers => new ( _values , TagHelpersIndex ) ;
49- public Property < ISet < TagHelperDescriptor > > ReferencedTagHelpers => new ( _values , ReferencedTagHelpersIndex ) ;
50- public Property < RazorSyntaxTree > PreTagHelperSyntaxTree => new ( _values , PreTagHelperSyntaxTreeIndex ) ;
51- public Property < RazorSyntaxTree > SyntaxTree => new ( _values , SyntaxTreeIndex ) ;
52- public BoxedProperty < ImmutableArray < RazorSyntaxTree > > ImportSyntaxTrees => new ( _values , ImportSyntaxTreesIndex ) ;
53- public Property < TagHelperDocumentContext > TagHelperContext => new ( _values , TagHelperContextIndex ) ;
54- public Property < DocumentIntermediateNode > DocumentNode => new ( _values , DocumentIntermediateNodeIndex ) ;
55- public Property < RazorCSharpDocument > CSharpDocument => new ( _values , CSharpDocumentIndex ) ;
56- public Property < RazorHtmlDocument > HtmlDocument => new ( _values , HtmlDocumentIndex ) ;
57- public BoxedProperty < ( string name , SourceSpan ? span ) > NamespaceInfo => new ( _values , NamespaceInfoIndex ) ;
58-
49+ /// <summary>
50+ /// Provides access to a specific slot within an array for a given reference type.
51+ /// </summary>
52+ /// <param name="values">The array of values.</param>
53+ /// <param name="index">The index within <paramref name="values"/> to access.</param>
54+ /// <remarks>
55+ /// A <see langword="null"/> value in the slot indicates that the value is not present.
56+ /// </remarks>
5957 public readonly ref struct Property < T > ( object ? [ ] values , int index )
6058 where T : class
6159 {
60+ // We can use a ref field to access the array slot directly on modern .NET.
61+ // On NetFx, we index into the array for each access.
62+ #if NET
63+ private readonly ref object ? _value = ref values [ index ] ;
64+ #endif
65+
6266 public T ? Value
67+ #if NET
68+ => ( T ? ) _value ;
69+ #else
6370 => ( T ? ) values[ index ] ;
71+ #endif
6472
6573 public void SetValue ( T ? value )
74+ #if NET
75+ => _value = value ;
76+ #else
6677 => values [ index ] = value ;
78+ #endif
6779
6880 public bool TryGetValue ( [ NotNullWhen ( true ) ] out T ? result )
6981 {
@@ -75,16 +87,22 @@ public T RequiredValue
7587 => Value . AssumeNotNull ( ) ;
7688 }
7789
90+ /// <summary>
91+ /// Provides access to a specific slot within an array for a given value type.
92+ /// A <see cref="StrongBox{T}"/> is employed to avoid boxing and unboxing the value.
93+ /// </summary>
94+ /// <param name="values">The array of values.</param>
95+ /// <param name="index">The index within <paramref name="values"/> to access.</param>
7896 public readonly ref struct BoxedProperty < T > ( object ? [ ] values , int index )
7997 where T : struct
8098 {
81- private Property < StrongBox < T > > StrongBox => new ( values , index ) ;
99+ private readonly Property < StrongBox < T > > _box = new ( values , index ) ;
82100
83- public T ? Value => StrongBox . Value ? . Value ;
101+ public T ? Value => _box . Value ? . Value ;
84102
85103 public bool TryGetValue ( out T result )
86104 {
87- if ( StrongBox . TryGetValue ( out var box ) )
105+ if ( _box . TryGetValue ( out var box ) )
88106 {
89107 result = box . Value ;
90108 return true ;
@@ -96,7 +114,7 @@ public bool TryGetValue(out T result)
96114
97115 public void SetValue ( T value )
98116 {
99- if ( StrongBox . TryGetValue ( out var box ) )
117+ if ( _box . TryGetValue ( out var box ) )
100118 {
101119 // If we've already created a StrongBox, just update the value.
102120 box . Value = value ;
@@ -105,7 +123,7 @@ public void SetValue(T value)
105123 {
106124 // Otherwise, create a new StrongBox.
107125 box = new ( value ) ;
108- StrongBox . SetValue ( box ) ;
126+ _box . SetValue ( box ) ;
109127 }
110128 }
111129 }
0 commit comments