Skip to content

Commit 96f1ad1

Browse files
committed
Cache reflection lookups in DynamicDataOperations
The Get*ConsideringInheritance methods (for fields, properties, and methods) walk the full type hierarchy via reflection on every call. This adds a ConcurrentDictionary cache per member kind, keyed by a (Type, memberName) struct, so the hierarchy walk happens only once per unique pair. Uses a custom TypeMemberKey struct instead of ValueTuple for net462 compatibility.
1 parent ad877cd commit 96f1ad1

1 file changed

Lines changed: 40 additions & 2 deletions

File tree

src/TestFramework/TestFramework/Attributes/DataSource/DynamicDataOperations.cs

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ internal static class DynamicDataOperations
77
{
88
private const BindingFlags DeclaredOnlyLookup = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly;
99

10+
private static readonly ConcurrentDictionary<TypeMemberKey, FieldInfo?> FieldCache = new();
11+
private static readonly ConcurrentDictionary<TypeMemberKey, PropertyInfo?> PropertyCache = new();
12+
private static readonly ConcurrentDictionary<TypeMemberKey, MethodInfo?> MethodCache = new();
13+
1014
public static IEnumerable<object[]> GetData(Type? dynamicDataDeclaringType, DynamicDataSourceType dynamicDataSourceType, string dynamicDataSourceName, object?[] dynamicDataSourceArguments, MethodInfo methodInfo)
1115
{
1216
// Check if the declaring type of test data is passed in. If not, default to test method's class type.
@@ -167,6 +171,15 @@ private static bool TryGetData(object dataSource, [NotNullWhen(true)] out IEnume
167171
}
168172

169173
private static FieldInfo? GetFieldConsideringInheritance(Type type, string fieldName)
174+
=> FieldCache.GetOrAdd(new TypeMemberKey(type, fieldName), static key => LookupFieldInHierarchy(key.Type, key.Name));
175+
176+
private static PropertyInfo? GetPropertyConsideringInheritance(Type type, string propertyName)
177+
=> PropertyCache.GetOrAdd(new TypeMemberKey(type, propertyName), static key => LookupPropertyInHierarchy(key.Type, key.Name));
178+
179+
private static MethodInfo? GetMethodConsideringInheritance(Type type, string methodName)
180+
=> MethodCache.GetOrAdd(new TypeMemberKey(type, methodName), static key => LookupMethodInHierarchy(key.Type, key.Name));
181+
182+
private static FieldInfo? LookupFieldInHierarchy(Type type, string fieldName)
170183
{
171184
// NOTE: Don't use GetRuntimeField. It considers inheritance only for instance fields.
172185
Type? currentType = type;
@@ -184,7 +197,7 @@ private static bool TryGetData(object dataSource, [NotNullWhen(true)] out IEnume
184197
return null;
185198
}
186199

187-
private static PropertyInfo? GetPropertyConsideringInheritance(Type type, string propertyName)
200+
private static PropertyInfo? LookupPropertyInHierarchy(Type type, string propertyName)
188201
{
189202
// NOTE: Don't use GetRuntimeProperty. It considers inheritance only for instance properties.
190203
Type? currentType = type;
@@ -202,7 +215,7 @@ private static bool TryGetData(object dataSource, [NotNullWhen(true)] out IEnume
202215
return null;
203216
}
204217

205-
private static MethodInfo? GetMethodConsideringInheritance(Type type, string methodName)
218+
private static MethodInfo? LookupMethodInHierarchy(Type type, string methodName)
206219
{
207220
// NOTE: Don't use GetRuntimeMethod. It considers inheritance only for instance methods.
208221
Type? currentType = type;
@@ -219,4 +232,29 @@ private static bool TryGetData(object dataSource, [NotNullWhen(true)] out IEnume
219232

220233
return null;
221234
}
235+
236+
private readonly struct TypeMemberKey : IEquatable<TypeMemberKey>
237+
{
238+
public TypeMemberKey(Type type, string name)
239+
{
240+
Type = type;
241+
Name = name;
242+
}
243+
244+
public Type Type { get; }
245+
246+
public string Name { get; }
247+
248+
public bool Equals(TypeMemberKey other) => Type == other.Type && Name == other.Name;
249+
250+
public override bool Equals(object? obj) => obj is TypeMemberKey other && Equals(other);
251+
252+
public override int GetHashCode()
253+
{
254+
int h1 = Type.GetHashCode();
255+
int h2 = StringComparer.Ordinal.GetHashCode(Name);
256+
257+
return ((h1 << 5) + h1) ^ h2;
258+
}
259+
}
222260
}

0 commit comments

Comments
 (0)