Skip to content

Commit e0a4f72

Browse files
Better caching of parameter writer lookups using object-identity dictionary
1 parent 3ba4372 commit e0a4f72

File tree

1 file changed

+44
-43
lines changed

1 file changed

+44
-43
lines changed

src/Components/Components/src/Reflection/ComponentProperties.cs

Lines changed: 44 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,36 @@
77
using System.Diagnostics.CodeAnalysis;
88
using System.Linq;
99
using System.Reflection;
10+
using System.Runtime.CompilerServices;
1011

1112
namespace Microsoft.AspNetCore.Components.Reflection
1213
{
14+
internal readonly struct ObjectWithReferenceEquality : IEquatable<ObjectWithReferenceEquality>
15+
{
16+
private readonly object _value;
17+
18+
public ObjectWithReferenceEquality(object value)
19+
{
20+
_value = value;
21+
}
22+
23+
public override bool Equals(object? obj)
24+
{
25+
return obj is ObjectWithReferenceEquality other
26+
? ReferenceEquals(_value, other._value) : false;
27+
}
28+
29+
public bool Equals([AllowNull] ObjectWithReferenceEquality other)
30+
{
31+
return ReferenceEquals(_value, other._value);
32+
}
33+
34+
public override int GetHashCode()
35+
{
36+
return RuntimeHelpers.GetHashCode(_value);
37+
}
38+
}
39+
1340
internal static class ComponentProperties
1441
{
1542
private const BindingFlags _bindablePropertyFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.IgnoreCase;
@@ -20,36 +47,6 @@ internal static class ComponentProperties
2047
private readonly static ConcurrentDictionary<Type, WritersForType> _cachedWritersByType
2148
= new ConcurrentDictionary<Type, WritersForType>();
2249

23-
private static bool TryGetWriter(WriterEntry[] entries, string key, out IPropertySetter value)
24-
{
25-
int minNum = 0;
26-
int maxNum = entries.Length - 1;
27-
var comparer = StringComparer.Ordinal;
28-
29-
while (minNum <= maxNum)
30-
{
31-
int mid = (minNum + maxNum) / 2;
32-
ref var entryAtMidpoint = ref entries[mid];
33-
var comparisonResult = comparer.Compare(key, entryAtMidpoint.Key);
34-
if (comparisonResult < 0)
35-
{
36-
maxNum = mid - 1;
37-
}
38-
else if (comparisonResult > 0)
39-
{
40-
minNum = mid + 1;
41-
}
42-
else
43-
{
44-
value = entryAtMidpoint.Value;
45-
return true;
46-
}
47-
}
48-
49-
value = default!;
50-
return false;
51-
}
52-
5350
public static void SetProperties(in ParameterView parameters, object target)
5451
{
5552
if (target == null)
@@ -72,7 +69,7 @@ public static void SetProperties(in ParameterView parameters, object target)
7269
{
7370
var parameterName = parameter.Name;
7471

75-
if (!TryGetWriter(writers.WritersByName, parameterName, out var writer))
72+
if (!writers.TryGetValue(parameterName, out var writer))
7673
{
7774
// Case 1: There is nowhere to put this value.
7875
ThrowForUnknownIncomingParameterName(targetType, parameterName);
@@ -113,7 +110,7 @@ public static void SetProperties(in ParameterView parameters, object target)
113110
isCaptureUnmatchedValuesParameterSetExplicitly = true;
114111
}
115112

116-
if (TryGetWriter(writers.WritersByName, parameterName, out var writer))
113+
if (writers.TryGetValue(parameterName, out var writer))
117114
{
118115
if (!writer.Cascading && parameter.Cascading)
119116
{
@@ -278,7 +275,7 @@ private class WritersForType
278275
{
279276
public WritersForType(Type targetType)
280277
{
281-
var writersByNameDict = new Dictionary<string, IPropertySetter>();
278+
var writersByNameDict = new Dictionary<string, IPropertySetter>(StringComparer.OrdinalIgnoreCase);
282279
foreach (var propertyInfo in GetCandidateBindableProperties(targetType))
283280
{
284281
var parameterAttribute = propertyInfo.GetCustomAttribute<ParameterAttribute>();
@@ -328,23 +325,27 @@ public WritersForType(Type targetType)
328325
}
329326
}
330327

331-
WritersByName = writersByNameDict
332-
.Select(kv => new WriterEntry { Key = kv.Key, Value = kv.Value })
333-
.OrderBy(e => e.Key)
334-
.ToArray();
328+
_writersByName = writersByNameDict;
335329
}
336330

337-
public WriterEntry[] WritersByName { get; }
331+
private readonly Dictionary<string, IPropertySetter> _writersByName;
332+
private readonly Dictionary<ObjectWithReferenceEquality, IPropertySetter> _cachedLookups = new Dictionary<ObjectWithReferenceEquality, IPropertySetter>();
338333

339334
public IPropertySetter? CaptureUnmatchedValuesWriter { get; }
340335

341336
public string? CaptureUnmatchedValuesPropertyName { get; }
342-
}
343337

344-
private struct WriterEntry
345-
{
346-
public string Key;
347-
public IPropertySetter Value;
338+
public bool TryGetValue(string parameterName, out IPropertySetter setter)
339+
{
340+
var key = new ObjectWithReferenceEquality(parameterName);
341+
if (!_cachedLookups.TryGetValue(key, out setter!))
342+
{
343+
_writersByName.TryGetValue(parameterName, out setter!);
344+
_cachedLookups.Add(key, setter);
345+
}
346+
347+
return setter != null;
348+
}
348349
}
349350
}
350351
}

0 commit comments

Comments
 (0)