Got at least one data fetching method working; turns out, we can't use a patched LogicStack to get the data

This commit is contained in:
2026-01-14 22:11:11 +01:00
parent 40a8431464
commit 3f7122d30a
350 changed files with 41444 additions and 119 deletions

View File

@@ -0,0 +1,188 @@
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)
{
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>
/// Determines whether [contains] [the specified member].
/// </summary>
/// <typeparam name="T">The type of the attribute to be retrieved.</typeparam>
/// <param name="member">The member.</param>
/// <returns>
/// <c>true</c> if [contains] [the specified member]; otherwise, <c>false</c>.
/// </returns>
public bool Contains<T>(MemberInfo member) => _data.Value.ContainsKey(new Tuple<object, Type>(member, typeof(T)));
/// <summary>
/// Gets specific attributes from a member constrained to an attribute.
/// </summary>
/// <typeparam name="T">The type of the attribute to be retrieved.</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 array of the attributes stored for the specified type.</returns>
public IEnumerable<object> Retrieve<T>(MemberInfo member, bool inherit = false)
where T : Attribute
{
if (member == null)
throw new ArgumentNullException(nameof(member));
return Retrieve(new Tuple<object, Type>(member, typeof(T)), t => member.GetCustomAttributes<T>(inherit));
}
/// <summary>
/// Gets all attributes of a specific type from a member.
/// </summary>
/// <param name="member">The member.</param>
/// <param name="type">The attribute type.</param>
/// <param name="inherit"><c>true</c> to inspect the ancestors of element; otherwise, <c>false</c>.</param>
/// <returns>An array of the attributes stored for the specified type.</returns>
public IEnumerable<object> Retrieve(MemberInfo member, Type type, bool inherit = false)
{
if (member == null)
throw new ArgumentNullException(nameof(member));
if (type == null)
throw new ArgumentNullException(nameof(type));
return Retrieve(
new Tuple<object, Type>(member, type),
t => member.GetCustomAttributes(type, inherit));
}
/// <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, bool inherit = false)
where T : Attribute
{
if (member == null)
return default;
var attr = Retrieve(
new Tuple<object, Type>(member, typeof(T)),
t => member.GetCustomAttributes(typeof(T), inherit));
return ConvertToAttribute<T>(attr);
}
/// <summary>
/// Gets one attribute of a specific type from a generic type.
/// </summary>
/// <typeparam name="TAttribute">The type of the attribute.</typeparam>
/// <typeparam name="T">The type to retrieve the attribute.</typeparam>
/// <param name="inherit">if set to <c>true</c> [inherit].</param>
/// <returns>An attribute stored for the specified type.</returns>
public TAttribute RetrieveOne<TAttribute, T>(bool inherit = false)
where TAttribute : Attribute
{
var attr = Retrieve(
new Tuple<object, Type>(typeof(T), typeof(TAttribute)),
t => typeof(T).GetCustomAttributes(typeof(TAttribute), inherit));
return ConvertToAttribute<TAttribute>(attr);
}
/// <summary>
/// Gets all properties an their attributes of a given type constrained to only attributes.
/// </summary>
/// <typeparam name="T">The type of the attribute to retrieve.</typeparam>
/// <param name="type">The type of the object.</param>
/// <param name="inherit"><c>true</c> to inspect the ancestors of element; otherwise, <c>false</c>.</param>
/// <returns>A dictionary of the properties and their attributes stored for the specified type.</returns>
public Dictionary<PropertyInfo, IEnumerable<object>> Retrieve<T>(Type type, bool inherit = false)
where T : Attribute =>
PropertyTypeCache.RetrieveAllProperties(type, true)
.ToDictionary(x => x, x => Retrieve<T>(x, inherit));
/// <summary>
/// Gets all properties and their attributes of a given type.
/// </summary>
/// <typeparam name="T">The object type used to extract the properties from.</typeparam>
/// <typeparam name="TAttribute">The type of the attribute.</typeparam>
/// <param name="inherit"><c>true</c> to inspect the ancestors of element; otherwise, <c>false</c>.</param>
/// <returns>
/// A dictionary of the properties and their attributes stored for the specified type.
/// </returns>
public Dictionary<PropertyInfo, IEnumerable<object>> RetrieveFromType<T, TAttribute>(bool inherit = false)
=> RetrieveFromType<T>(typeof(TAttribute), inherit);
/// <summary>
/// Gets all properties and their attributes of a given type.
/// </summary>
/// <typeparam name="T">The object type used to extract the properties from.</typeparam>
/// <param name="attributeType">Type of the attribute.</param>
/// <param name="inherit"><c>true</c> to inspect the ancestors of element; otherwise, <c>false</c>.</param>
/// <returns>
/// A dictionary of the properties and their attributes stored for the specified type.
/// </returns>
public Dictionary<PropertyInfo, IEnumerable<object>> RetrieveFromType<T>(Type attributeType, bool inherit = false)
{
if (attributeType == null)
throw new ArgumentNullException(nameof(attributeType));
return PropertyTypeCache.RetrieveAllProperties<T>(true)
.ToDictionary(x => x, x => Retrieve(x, attributeType, inherit));
}
private static T ConvertToAttribute<T>(IEnumerable<object> attr)
where T : Attribute
{
if (attr?.Any() != true)
return default;
return 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 _data.Value.GetOrAdd(key, k => factory.Invoke(k).Where(item => item != null));
}
}
}

View File

@@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace Swan.Reflection
{
/// <summary>
/// A thread-safe cache of constructors belonging to a given type.
/// </summary>
public class ConstructorTypeCache : TypeCache<Tuple<ConstructorInfo, ParameterInfo[]>>
{
/// <summary>
/// Gets the default cache.
/// </summary>
/// <value>
/// The default cache.
/// </value>
public static Lazy<ConstructorTypeCache> DefaultCache { get; } =
new Lazy<ConstructorTypeCache>(() => new ConstructorTypeCache());
/// <summary>
/// Retrieves all constructors order by the number of parameters ascending.
/// </summary>
/// <typeparam name="T">The type to inspect.</typeparam>
/// <param name="includeNonPublic">if set to <c>true</c> [include non public].</param>
/// <returns>
/// A collection with all the constructors in the given type.
/// </returns>
public IEnumerable<Tuple<ConstructorInfo, ParameterInfo[]>> RetrieveAllConstructors<T>(bool includeNonPublic = false)
=> Retrieve<T>(GetConstructors(includeNonPublic));
/// <summary>
/// Retrieves all constructors order by the number of parameters ascending.
/// </summary>
/// <param name="type">The type.</param>
/// <param name="includeNonPublic">if set to <c>true</c> [include non public].</param>
/// <returns>
/// A collection with all the constructors in the given type.
/// </returns>
public IEnumerable<Tuple<ConstructorInfo, ParameterInfo[]>> RetrieveAllConstructors(Type type, bool includeNonPublic = false)
=> Retrieve(type, GetConstructors(includeNonPublic));
private static Func<Type, IEnumerable<Tuple<ConstructorInfo, ParameterInfo[]>>> GetConstructors(bool includeNonPublic)
=> t => t.GetConstructors(includeNonPublic ? BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance : BindingFlags.Public | BindingFlags.Instance)
.Select(x => Tuple.Create(x, x.GetParameters()))
.OrderBy(x => x.Item2.Length)
.ToList();
}
}

View File

@@ -0,0 +1,267 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Reflection;
namespace Swan.Reflection
{
/// <summary>
/// Provides extended information about a type.
///
/// This class is mainly used to define sets of types within the Definition class
/// and it is not meant for other than querying the BasicTypesInfo dictionary.
/// </summary>
public class ExtendedTypeInfo
{
private const string TryParseMethodName = nameof(byte.TryParse);
private const string ToStringMethodName = nameof(ToString);
private static readonly Type[] NumericTypes =
{
typeof(byte),
typeof(sbyte),
typeof(decimal),
typeof(double),
typeof(float),
typeof(int),
typeof(uint),
typeof(long),
typeof(ulong),
typeof(short),
typeof(ushort),
};
private readonly ParameterInfo[]? _tryParseParameters;
private readonly int _toStringArgumentLength;
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="ExtendedTypeInfo"/> class.
/// </summary>
/// <param name="t">The t.</param>
public ExtendedTypeInfo(Type t)
{
Type = t ?? throw new ArgumentNullException(nameof(t));
IsNullableValueType = Type.IsGenericType
&& Type.GetGenericTypeDefinition() == typeof(Nullable<>);
IsValueType = t.IsValueType;
UnderlyingType = IsNullableValueType ?
new NullableConverter(Type).UnderlyingType :
Type;
IsNumeric = NumericTypes.Contains(UnderlyingType);
// Extract the TryParse method info
try
{
TryParseMethodInfo = UnderlyingType.GetMethod(TryParseMethodName,
new[] { typeof(string), typeof(NumberStyles), typeof(IFormatProvider), UnderlyingType.MakeByRefType() }) ??
UnderlyingType.GetMethod(TryParseMethodName,
new[] { typeof(string), UnderlyingType.MakeByRefType() });
_tryParseParameters = TryParseMethodInfo?.GetParameters();
}
catch
{
// ignored
}
// Extract the ToString method Info
try
{
ToStringMethodInfo = UnderlyingType.GetMethod(ToStringMethodName,
new[] { typeof(IFormatProvider) }) ??
UnderlyingType.GetMethod(ToStringMethodName,
Array.Empty<Type>());
_toStringArgumentLength = ToStringMethodInfo?.GetParameters().Length ?? 0;
}
catch
{
// ignored
}
}
#endregion
#region Properties
/// <summary>
/// Gets the type this extended info class provides for.
/// </summary>
/// <value>
/// The type.
/// </value>
public Type Type { get; }
/// <summary>
/// Gets a value indicating whether the type is a nullable value type.
/// </summary>
/// <value>
/// <c>true</c> if this instance is nullable value type; otherwise, <c>false</c>.
/// </value>
public bool IsNullableValueType { get; }
/// <summary>
/// Gets a value indicating whether the type or underlying type is numeric.
/// </summary>
/// <value>
/// <c>true</c> if this instance is numeric; otherwise, <c>false</c>.
/// </value>
public bool IsNumeric { get; }
/// <summary>
/// Gets a value indicating whether the type is value type.
/// Nullable value types have this property set to False.
/// </summary>
public bool IsValueType { get; }
/// <summary>
/// When dealing with nullable value types, this property will
/// return the underlying value type of the nullable,
/// Otherwise it will return the same type as the Type property.
/// </summary>
/// <value>
/// The type of the underlying.
/// </value>
public Type UnderlyingType { get; }
/// <summary>
/// Gets the try parse method information. If the type does not contain
/// a suitable TryParse static method, it will return null.
/// </summary>
/// <value>
/// The try parse method information.
/// </value>
public MethodInfo TryParseMethodInfo { get; }
/// <summary>
/// Gets the ToString method info
/// It will prefer the overload containing the IFormatProvider argument.
/// </summary>
/// <value>
/// To string method information.
/// </value>
public MethodInfo ToStringMethodInfo { get; }
/// <summary>
/// Gets a value indicating whether the type contains a suitable TryParse method.
/// </summary>
/// <value>
/// <c>true</c> if this instance can parse natively; otherwise, <c>false</c>.
/// </value>
public bool CanParseNatively => TryParseMethodInfo != null;
#endregion
#region Methods
/// <summary>
/// Tries to parse the string into an object of the type this instance represents.
/// Returns false when no suitable TryParse methods exists for the type or when parsing fails
/// for any reason. When possible, this method uses CultureInfo.InvariantCulture and NumberStyles.Any.
/// </summary>
/// <param name="s">The s.</param>
/// <param name="result">The result.</param>
/// <returns><c>true</c> if parse was converted successfully; otherwise, <c>false</c>.</returns>
public bool TryParse(string s, out object? result)
{
result = Type.GetDefault();
try
{
if (Type == typeof(string))
{
result = Convert.ChangeType(s, Type, CultureInfo.InvariantCulture);
return true;
}
if ((IsNullableValueType && string.IsNullOrEmpty(s)) || !CanParseNatively)
{
return true;
}
// Build the arguments of the TryParse method
var dynamicArguments = new List<object?> { s };
for (var pi = 1; pi < _tryParseParameters.Length - 1; pi++)
{
var argInfo = _tryParseParameters[pi];
if (argInfo.ParameterType == typeof(IFormatProvider))
dynamicArguments.Add(CultureInfo.InvariantCulture);
else if (argInfo.ParameterType == typeof(NumberStyles))
dynamicArguments.Add(NumberStyles.Any);
else
dynamicArguments.Add(null);
}
dynamicArguments.Add(null);
var parseArguments = dynamicArguments.ToArray();
if ((bool) TryParseMethodInfo.Invoke(null, parseArguments))
{
result = parseArguments[parseArguments.Length - 1];
return true;
}
}
catch
{
// Ignore
}
return false;
}
/// <summary>
/// Converts this instance to its string representation,
/// trying to use the CultureInfo.InvariantCulture
/// IFormat provider if the overload is available.
/// </summary>
/// <param name="instance">The instance.</param>
/// <returns>A <see cref="System.String" /> that represents the current object.</returns>
public string ToStringInvariant(object instance)
{
if (instance == null)
return string.Empty;
return _toStringArgumentLength != 1
? instance.ToString()
: ToStringMethodInfo.Invoke(instance, new object[] {CultureInfo.InvariantCulture}) as string ?? string.Empty;
}
#endregion
}
/// <summary>
/// Provides extended information about a type.
///
/// This class is mainly used to define sets of types within the Constants class
/// and it is not meant for other than querying the BasicTypesInfo dictionary.
/// </summary>
/// <typeparam name="T">The type of extended type information.</typeparam>
public class ExtendedTypeInfo<T> : ExtendedTypeInfo
{
/// <summary>
/// Initializes a new instance of the <see cref="ExtendedTypeInfo{T}"/> class.
/// </summary>
public ExtendedTypeInfo()
: base(typeof(T))
{
// placeholder
}
/// <summary>
/// Converts this instance to its string representation,
/// trying to use the CultureInfo.InvariantCulture
/// IFormat provider if the overload is available.
/// </summary>
/// <param name="instance">The instance.</param>
/// <returns>A <see cref="System.String" /> that represents the current object.</returns>
public string ToStringInvariant(T instance) => base.ToStringInvariant(instance);
}
}

View File

@@ -0,0 +1,45 @@
using System;
using System.Reflection;
namespace Swan.Reflection
{
/// <summary>
/// Represents a generic interface to store getters and setters for high speed access to properties.
/// </summary>
public interface IPropertyProxy
{
/// <summary>
/// Gets the name of the property.
/// </summary>
string Name { get; }
/// <summary>
/// Gets the type of the property.
/// </summary>
Type PropertyType { get; }
/// <summary>
/// Gets the associated reflection property info.
/// </summary>
PropertyInfo Property { get; }
/// <summary>
/// Gets the type owning this property proxy.
/// </summary>
Type EnclosingType { get; }
/// <summary>
/// Gets the property value via a stored delegate.
/// </summary>
/// <param name="instance">The instance.</param>
/// <returns>The property value.</returns>
object? GetValue(object instance);
/// <summary>
/// Sets the property value via a stored delegate.
/// </summary>
/// <param name="instance">The instance.</param>
/// <param name="value">The value.</param>
void SetValue(object instance, object? value);
}
}

View File

@@ -0,0 +1,117 @@
using System;
using System.Collections.Concurrent;
using System.Reflection;
namespace Swan.Reflection
{
/// <summary>
/// Represents a Method Info Cache.
/// </summary>
public class MethodInfoCache : ConcurrentDictionary<string, MethodInfo>
{
/// <summary>
/// Retrieves the properties stored for the specified type.
/// If the properties are not available, it calls the factory method to retrieve them
/// and returns them as an array of PropertyInfo.
/// </summary>
/// <typeparam name="T">The type of type.</typeparam>
/// <param name="name">The name.</param>
/// <param name="alias">The alias.</param>
/// <param name="types">The types.</param>
/// <returns>
/// The cached MethodInfo.
/// </returns>
/// <exception cref="ArgumentNullException">name
/// or
/// factory.</exception>
public MethodInfo Retrieve<T>(string name, string alias, params Type[] types)
=> Retrieve(typeof(T), name, alias, types);
/// <summary>
/// Retrieves the specified name.
/// </summary>
/// <typeparam name="T">The type of type.</typeparam>
/// <param name="name">The name.</param>
/// <param name="types">The types.</param>
/// <returns>
/// The cached MethodInfo.
/// </returns>
public MethodInfo Retrieve<T>(string name, params Type[] types)
=> Retrieve(typeof(T), name, name, types);
/// <summary>
/// Retrieves the specified type.
/// </summary>
/// <param name="type">The type.</param>
/// <param name="name">The name.</param>
/// <param name="types">The types.</param>
/// <returns>
/// An array of the properties stored for the specified type.
/// </returns>
public MethodInfo Retrieve(Type type, string name, params Type[] types)
=> Retrieve(type, name, name, types);
/// <summary>
/// Retrieves the specified type.
/// </summary>
/// <param name="type">The type.</param>
/// <param name="name">The name.</param>
/// <param name="alias">The alias.</param>
/// <param name="types">The types.</param>
/// <returns>
/// The cached MethodInfo.
/// </returns>
public MethodInfo Retrieve(Type type, string name, string alias, params Type[] types)
{
if (type == null)
throw new ArgumentNullException(nameof(type));
if (alias == null)
throw new ArgumentNullException(nameof(alias));
if (name == null)
throw new ArgumentNullException(nameof(name));
return GetOrAdd(
alias,
x => type.GetMethod(name, types ?? Array.Empty<Type>()));
}
/// <summary>
/// Retrieves the specified name.
/// </summary>
/// <typeparam name="T">The type of type.</typeparam>
/// <param name="name">The name.</param>
/// <returns>
/// The cached MethodInfo.
/// </returns>
public MethodInfo Retrieve<T>(string name)
=> Retrieve(typeof(T), name);
/// <summary>
/// Retrieves the specified type.
/// </summary>
/// <param name="type">The type.</param>
/// <param name="name">The name.</param>
/// <returns>
/// The cached MethodInfo.
/// </returns>
/// <exception cref="ArgumentNullException">
/// type
/// or
/// name.
/// </exception>
public MethodInfo Retrieve(Type type, string name)
{
if (type == null)
throw new ArgumentNullException(nameof(type));
if (name == null)
throw new ArgumentNullException(nameof(name));
return GetOrAdd(
name,
type.GetMethod);
}
}
}

View File

@@ -0,0 +1,79 @@
using System;
using System.Linq.Expressions;
using System.Reflection;
namespace Swan.Reflection
{
/// <summary>
/// The concrete and hidden implementation of the <see cref="IPropertyProxy"/> implementation.
/// </summary>
/// <seealso cref="IPropertyProxy" />
internal sealed class PropertyInfoProxy : IPropertyProxy
{
private readonly Func<object, object>? _getter;
private readonly Action<object, object?>? _setter;
/// <summary>
/// Initializes a new instance of the <see cref="PropertyInfoProxy"/> class.
/// </summary>
/// <param name="declaringType">Type of the declaring.</param>
/// <param name="propertyInfo">The property information.</param>
public PropertyInfoProxy(Type declaringType, PropertyInfo propertyInfo)
{
Property = propertyInfo;
EnclosingType = declaringType;
_getter = CreateLambdaGetter(declaringType, propertyInfo);
_setter = CreateLambdaSetter(declaringType, propertyInfo);
}
/// <inheritdoc />
public PropertyInfo Property { get; }
/// <inheritdoc />
public Type EnclosingType { get; }
/// <inheritdoc />
public string Name => Property.Name;
/// <inheritdoc />
public Type PropertyType => Property.PropertyType;
/// <inheritdoc />
public object? GetValue(object instance) => _getter?.Invoke(instance);
/// <inheritdoc />
public void SetValue(object instance, object? value) => _setter?.Invoke(instance, value);
private static Func<object, object>? CreateLambdaGetter(Type instanceType, PropertyInfo propertyInfo)
{
if (!propertyInfo.CanRead)
return null;
var instanceParameter = Expression.Parameter(typeof(object), "instance");
var typedInstance = Expression.Convert(instanceParameter, instanceType);
var property = Expression.Property(typedInstance, propertyInfo);
var convert = Expression.Convert(property, typeof(object));
var dynamicGetter = (Func<object, object>)Expression.Lambda(convert, instanceParameter).Compile();
return dynamicGetter;
}
private static Action<object, object?>? CreateLambdaSetter(Type instanceType, PropertyInfo propertyInfo)
{
if (!propertyInfo.CanWrite)
return null;
var instanceParameter = Expression.Parameter(typeof(object), "instance");
var valueParameter = Expression.Parameter(typeof(object), "value");
var typedInstance = Expression.Convert(instanceParameter, instanceType);
var property = Expression.Property(typedInstance, propertyInfo);
var propertyValue = Expression.Convert(valueParameter, propertyInfo.PropertyType);
var body = Expression.Assign(property, propertyValue);
var dynamicSetter = Expression.Lambda<Action<object, object?>>(body, instanceParameter, valueParameter).Compile();
return dynamicSetter;
}
}
}

View File

@@ -0,0 +1,74 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace Swan.Reflection
{
/// <summary>
/// A thread-safe cache of properties belonging to a given type.
/// </summary>
public class PropertyTypeCache : TypeCache<PropertyInfo>
{
/// <summary>
/// Gets the default cache.
/// </summary>
/// <value>
/// The default cache.
/// </value>
public static Lazy<PropertyTypeCache> DefaultCache { get; } = new Lazy<PropertyTypeCache>(() => new PropertyTypeCache());
/// <summary>
/// Retrieves all properties.
/// </summary>
/// <typeparam name="T">The type to inspect.</typeparam>
/// <param name="onlyPublic">if set to <c>true</c> [only public].</param>
/// <returns>
/// A collection with all the properties in the given type.
/// </returns>
public IEnumerable<PropertyInfo> RetrieveAllProperties<T>(bool onlyPublic = false)
=> Retrieve<T>(onlyPublic ? GetAllPublicPropertiesFunc() : GetAllPropertiesFunc());
/// <summary>
/// Retrieves all properties.
/// </summary>
/// <param name="type">The type.</param>
/// <param name="onlyPublic">if set to <c>true</c> [only public].</param>
/// <returns>
/// A collection with all the properties in the given type.
/// </returns>
public IEnumerable<PropertyInfo> RetrieveAllProperties(Type type, bool onlyPublic = false)
=> Retrieve(type, onlyPublic ? GetAllPublicPropertiesFunc() : GetAllPropertiesFunc());
/// <summary>
/// Retrieves the filtered properties.
/// </summary>
/// <param name="type">The type.</param>
/// <param name="onlyPublic">if set to <c>true</c> [only public].</param>
/// <param name="filter">The filter.</param>
/// <returns>
/// A collection with all the properties in the given type.
/// </returns>
public IEnumerable<PropertyInfo> RetrieveFilteredProperties(
Type type,
bool onlyPublic,
Func<PropertyInfo, bool> filter)
=> Retrieve(type,
onlyPublic ? GetAllPublicPropertiesFunc(filter) : GetAllPropertiesFunc(filter));
private static Func<Type, IEnumerable<PropertyInfo>> GetAllPropertiesFunc(
Func<PropertyInfo, bool>? filter = null)
=> GetPropertiesFunc(
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance,
filter);
private static Func<Type, IEnumerable<PropertyInfo>> GetAllPublicPropertiesFunc(
Func<PropertyInfo, bool>? filter = null)
=> GetPropertiesFunc(BindingFlags.Public | BindingFlags.Instance, filter);
private static Func<Type, IEnumerable<PropertyInfo>> GetPropertiesFunc(BindingFlags flags,
Func<PropertyInfo, bool>? filter = null)
=> t => t.GetProperties(flags)
.Where(filter ?? (p => p.CanRead || p.CanWrite));
}
}

View File

@@ -0,0 +1,78 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using Swan.Collections;
namespace Swan.Reflection
{
/// <summary>
/// A thread-safe cache of members belonging to a given 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>
/// <typeparam name="T">The type of Member to be cached.</typeparam>
public abstract class TypeCache<T> : CollectionCacheRepository<T>
{
/// <summary>
/// Determines whether the cache contains the specified type.
/// </summary>
/// <typeparam name="TOut">The type of the out.</typeparam>
/// <returns>
/// <c>true</c> if [contains]; otherwise, <c>false</c>.
/// </returns>
public bool Contains<TOut>() => ContainsKey(typeof(TOut));
/// <summary>
/// Retrieves the properties stored for the specified type.
/// If the properties are not available, it calls the factory method to retrieve them
/// and returns them as an array of PropertyInfo.
/// </summary>
/// <typeparam name="TOut">The type of the out.</typeparam>
/// <param name="factory">The factory.</param>
/// <returns>An array of the properties stored for the specified type.</returns>
public IEnumerable<T> Retrieve<TOut>(Func<Type, IEnumerable<T>> factory)
=> Retrieve(typeof(TOut), factory);
}
/// <summary>
/// A thread-safe cache of fields belonging to a given 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 FieldTypeCache : TypeCache<FieldInfo>
{
/// <summary>
/// Gets the default cache.
/// </summary>
/// <value>
/// The default cache.
/// </value>
public static Lazy<FieldTypeCache> DefaultCache { get; } = new Lazy<FieldTypeCache>(() => new FieldTypeCache());
/// <summary>
/// Retrieves all fields.
/// </summary>
/// <typeparam name="T">The type to inspect.</typeparam>
/// <returns>
/// A collection with all the fields in the given type.
/// </returns>
public IEnumerable<FieldInfo> RetrieveAllFields<T>()
=> Retrieve<T>(GetAllFieldsFunc());
/// <summary>
/// Retrieves all fields.
/// </summary>
/// <param name="type">The type.</param>
/// <returns>
/// A collection with all the fields in the given type.
/// </returns>
public IEnumerable<FieldInfo> RetrieveAllFields(Type type)
=> Retrieve(type, GetAllFieldsFunc());
private static Func<Type, IEnumerable<FieldInfo>> GetAllFieldsFunc()
=> t => t.GetFields(BindingFlags.Public | BindingFlags.Instance);
}
}