Skip to content

Commit d93a868

Browse files
authored
Merge pull request #298 from servicetitan/upstream/AttributeHelper
Optimization: avoid 2-level cache in AttributeHelper
2 parents baa8098 + bb1fb8c commit d93a868

1 file changed

Lines changed: 56 additions & 71 deletions

File tree

Orm/Xtensive.Orm/Reflection/AttributeHelper.cs

Lines changed: 56 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
using System.Linq;
1111
using System.Reflection;
1212
using Xtensive.Core;
13-
using AttributesKey = System.ValueTuple<System.Reflection.MemberInfo, System.Type, Xtensive.Reflection.AttributeSearchOptions>;
1413
using PerAttributeKey = System.ValueTuple<System.Reflection.MemberInfo, Xtensive.Reflection.AttributeSearchOptions>;
1514

1615
namespace Xtensive.Reflection
@@ -20,22 +19,65 @@ namespace Xtensive.Reflection
2019
/// </summary>
2120
public static class AttributeHelper
2221
{
23-
internal class AttributeExtractors<TAttribute> where TAttribute : Attribute
24-
{
25-
internal static readonly Func<PerAttributeKey, TAttribute[]> AttributesExtractor = key => {
26-
var uncasted = GetAttributes(key.Item1, typeof(TAttribute), key.Item2);
27-
return uncasted.Count > 0
28-
? uncasted.Cast<TAttribute>().ToArray(uncasted.Count)
29-
: Array.Empty<TAttribute>();
30-
};
31-
}
32-
3322
private static class AttributeDictionary<TAttribute> where TAttribute : Attribute
3423
{
24+
private static readonly Type attributeType = typeof(TAttribute);
3525
public static readonly ConcurrentDictionary<PerAttributeKey, TAttribute[]> Dictionary = new();
36-
}
3726

38-
private static readonly ConcurrentDictionary<AttributesKey, IReadOnlyList<Attribute>> AttributesByMemberInfoAndSearchOptions = new();
27+
public static readonly Func<PerAttributeKey, TAttribute[]> AttributesExtractor = ExtractAttributesByKey;
28+
29+
private static TAttribute[] ExtractAttributesByKey(PerAttributeKey key)
30+
{
31+
var (member, options) = key;
32+
33+
var attributesAsObjects = member.GetCustomAttributes(attributeType, false);
34+
var attributesCount = attributesAsObjects.Length;
35+
36+
var attributes = attributesCount > 0
37+
? attributesAsObjects.Cast<TAttribute>().ToList(attributesCount)
38+
: null;
39+
40+
if (options != AttributeSearchOptions.InheritNone) {
41+
if (attributesCount == 0) {
42+
if ((options & AttributeSearchOptions.InheritFromPropertyOrEvent) != 0
43+
&& member is MethodInfo m
44+
&& ((MemberInfo) m.GetProperty() ?? m.GetEvent()) is MemberInfo poe) {
45+
attributes = GetAttributesAsNewList(poe);
46+
}
47+
if ((options & AttributeSearchOptions.InheritFromBase) != 0
48+
&& (options & AttributeSearchOptions.InheritFromAllBase) == 0) {
49+
AddAttributesFromBase(ref attributes, member, options);
50+
}
51+
}
52+
53+
if ((options & AttributeSearchOptions.InheritFromAllBase) != 0
54+
&& member.DeclaringType != WellKnownTypes.Object) {
55+
AddAttributesFromBase(ref attributes, member, options);
56+
}
57+
}
58+
59+
return attributes?.ToArray(attributes.Count) ?? Array.Empty<TAttribute>();
60+
}
61+
62+
private static List<TAttribute> GetAttributesAsNewList(MemberInfo member)
63+
{
64+
var attrObjects = member.GetCustomAttributes(attributeType, false);
65+
var attrs = new List<TAttribute>(attrObjects.Length);
66+
for (int i = 0, count = attrObjects.Length; i < count; ++i) {
67+
attrs.Add((TAttribute) attrObjects[i]);
68+
}
69+
return attrs;
70+
}
71+
72+
private static void AddAttributesFromBase(ref List<TAttribute> attributes, MemberInfo member, AttributeSearchOptions options)
73+
{
74+
if (member.GetBaseMember() is MemberInfo bm) {
75+
var attrsToAdd = bm.GetAttributes<TAttribute>(options);
76+
if (attrsToAdd.Count > 0) {
77+
(attributes ??= new List<TAttribute>(attrsToAdd.Count)).AddRange(attrsToAdd);
78+
}
79+
}
80+
}
3981

4082
/// <summary>
4183
/// A shortcut to <see cref="MemberInfo.GetCustomAttributes(Type,bool)"/> method.
@@ -47,7 +89,7 @@ private static class AttributeDictionary<TAttribute> where TAttribute : Attribut
4789
///
4890
public static IReadOnlyList<TAttribute> GetAttributes<TAttribute>(this MemberInfo member, AttributeSearchOptions options = AttributeSearchOptions.InheritNone)
4991
where TAttribute : Attribute =>
50-
AttributeDictionary<TAttribute>.Dictionary.GetOrAdd(new PerAttributeKey(member, options), AttributeExtractors<TAttribute>.AttributesExtractor);
92+
AttributeDictionary<TAttribute>.Dictionary.GetOrAdd(new PerAttributeKey(member, options), AttributeDictionary<TAttribute>.AttributesExtractor);
5193

5294
/// <summary>
5395
/// A version of <see cref="GetAttributes{TAttribute}(MemberInfo, AttributeSearchOptions)"/>
@@ -73,62 +115,5 @@ public static TAttribute GetAttribute<TAttribute>(this MemberInfo member, Attrib
73115
typeof(TAttribute).GetShortName()))
74116
};
75117
}
76-
77-
private static IReadOnlyList<Attribute> GetAttributes(MemberInfo member, Type attributeType, AttributeSearchOptions options) =>
78-
AttributesByMemberInfoAndSearchOptions.GetOrAdd(
79-
new AttributesKey(member, attributeType, options),
80-
ExtractAttributes
81-
);
82-
83-
private static List<Attribute> GetAttributesAsNewList(this MemberInfo member, Type attributeType)
84-
{
85-
var attrObjects = member.GetCustomAttributes(attributeType, false);
86-
var attrs = new List<Attribute>(attrObjects.Length);
87-
for (int i = 0, count = attrObjects.Length; i < count; ++i) {
88-
attrs.Add((Attribute) attrObjects[i]);
89-
}
90-
return attrs;
91-
}
92-
93-
private static IReadOnlyList<Attribute> ExtractAttributes((MemberInfo member, Type attributeType, AttributeSearchOptions options) t)
94-
{
95-
(var member, var attributeType, var options) = t;
96-
97-
var attributesAsObjects = member.GetCustomAttributes(attributeType, false);
98-
var attributesCount = attributesAsObjects.Length;
99-
100-
var attributes = attributesCount > 0
101-
? attributesAsObjects.Cast<Attribute>().ToList(attributesCount)
102-
: null;
103-
104-
if (options != AttributeSearchOptions.InheritNone) {
105-
if (attributesCount == 0) {
106-
if ((options & AttributeSearchOptions.InheritFromPropertyOrEvent) != 0
107-
&& member is MethodInfo m
108-
&& ((MemberInfo) m.GetProperty() ?? m.GetEvent()) is MemberInfo poe) {
109-
attributes = poe.GetAttributesAsNewList(attributeType);
110-
}
111-
if ((options & AttributeSearchOptions.InheritFromBase) != 0
112-
&& (options & AttributeSearchOptions.InheritFromAllBase) == 0
113-
&& member.GetBaseMember() is MemberInfo bm) {
114-
var attrsToAdd = GetAttributes(bm, attributeType, options);
115-
if (attrsToAdd.Count > 0) {
116-
(attributes ??= new List<Attribute>(attrsToAdd.Count)).AddRange(attrsToAdd);
117-
}
118-
}
119-
}
120-
121-
if ((options & AttributeSearchOptions.InheritFromAllBase) != 0
122-
&& member.DeclaringType != WellKnownTypes.Object
123-
&& member.GetBaseMember() is MemberInfo bm2) {
124-
var attrsToAdd = GetAttributes(bm2, attributeType, options);
125-
if (attrsToAdd.Count > 0) {
126-
(attributes ??= new List<Attribute>(attrsToAdd.Count)).AddRange(attrsToAdd);
127-
}
128-
}
129-
}
130-
131-
return (IReadOnlyList<Attribute>) attributes ?? Array.Empty<Attribute>();
132-
}
133118
}
134119
}

0 commit comments

Comments
 (0)