68 lines
2.8 KiB
C#
68 lines
2.8 KiB
C#
#nullable enable
|
|
using System;
|
|
using System.Collections.Concurrent;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
|
|
namespace Swan.Reflection {
|
|
/// <summary>
|
|
/// A thread-safe cache of attributes belonging to a given key (MemberInfo or Type).
|
|
///
|
|
/// The Retrieve method is the most useful one in this class as it
|
|
/// calls the retrieval process if the type is not contained
|
|
/// in the cache.
|
|
/// </summary>
|
|
public class AttributeCache {
|
|
private readonly Lazy<ConcurrentDictionary<Tuple<Object, Type>, IEnumerable<Object>>> _data = new Lazy<ConcurrentDictionary<Tuple<Object, Type>, IEnumerable<Object>>>(() => new ConcurrentDictionary<Tuple<Object, Type>, IEnumerable<Object>>(), true);
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="AttributeCache"/> class.
|
|
/// </summary>
|
|
/// <param name="propertyCache">The property cache object.</param>
|
|
public AttributeCache(PropertyTypeCache? propertyCache = null) => this.PropertyTypeCache = propertyCache ?? PropertyTypeCache.DefaultCache.Value;
|
|
|
|
/// <summary>
|
|
/// Gets the default cache.
|
|
/// </summary>
|
|
/// <value>
|
|
/// The default cache.
|
|
/// </value>
|
|
public static Lazy<AttributeCache> DefaultCache { get; } = new Lazy<AttributeCache>(() => new AttributeCache());
|
|
|
|
/// <summary>
|
|
/// A PropertyTypeCache object for caching properties and their attributes.
|
|
/// </summary>
|
|
public PropertyTypeCache PropertyTypeCache {
|
|
get;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets one attribute of a specific type from a member.
|
|
/// </summary>
|
|
/// <typeparam name="T">The attribute type.</typeparam>
|
|
/// <param name="member">The member.</param>
|
|
/// <param name="inherit"><c>true</c> to inspect the ancestors of element; otherwise, <c>false</c>.</param>
|
|
/// <returns>An attribute stored for the specified type.</returns>
|
|
public T RetrieveOne<T>(MemberInfo member, Boolean inherit = false) where T : Attribute {
|
|
if(member == null) {
|
|
return default!;
|
|
}
|
|
|
|
IEnumerable<Object> attr = this.Retrieve(new Tuple<Object, Type>(member, typeof(T)), t => member.GetCustomAttributes(typeof(T), inherit));
|
|
|
|
return ConvertToAttribute<T>(attr);
|
|
}
|
|
|
|
private static T ConvertToAttribute<T>(IEnumerable<Object> attr) where T : Attribute => attr?.Any() != true ? (default!) : attr.Count() == 1 ? (T)Convert.ChangeType(attr.First(), typeof(T)) : throw new AmbiguousMatchException("Multiple custom attributes of the same type found.");
|
|
|
|
private IEnumerable<Object> Retrieve(Tuple<Object, Type> key, Func<Tuple<Object, Type>, IEnumerable<Object>> factory) {
|
|
if(factory == null) {
|
|
throw new ArgumentNullException(nameof(factory));
|
|
}
|
|
|
|
return this._data.Value.GetOrAdd(key, k => factory.Invoke(k).Where(item => item != null));
|
|
}
|
|
}
|
|
}
|