261 lines
9.3 KiB
C#
261 lines
9.3 KiB
C#
|
#nullable enable
|
|||
|
using System;
|
|||
|
using System.Collections.Concurrent;
|
|||
|
using System.Collections.Generic;
|
|||
|
using System.Linq;
|
|||
|
using System.Reflection;
|
|||
|
|
|||
|
namespace Swan {
|
|||
|
/// <summary>
|
|||
|
/// Provides various extension methods for Reflection and Types.
|
|||
|
/// </summary>
|
|||
|
public static class ReflectionExtensions {
|
|||
|
private static readonly Lazy<ConcurrentDictionary<Tuple<Boolean, PropertyInfo>, Func<Object, Object>>> CacheGetMethods = new Lazy<ConcurrentDictionary<Tuple<Boolean, PropertyInfo>, Func<Object, Object>>>(() => new ConcurrentDictionary<Tuple<Boolean, PropertyInfo>, Func<Object, Object>>(), true);
|
|||
|
|
|||
|
private static readonly Lazy<ConcurrentDictionary<Tuple<Boolean, PropertyInfo>, Action<Object, Object[]>>> CacheSetMethods = new Lazy<ConcurrentDictionary<Tuple<Boolean, PropertyInfo>, Action<Object, Object[]>>>(() => new ConcurrentDictionary<Tuple<Boolean, PropertyInfo>, Action<Object, Object[]>>(), true);
|
|||
|
|
|||
|
#region Type Extensions
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// The closest programmatic equivalent of default(T).
|
|||
|
/// </summary>
|
|||
|
/// <param name="type">The type.</param>
|
|||
|
/// <returns>
|
|||
|
/// Default value of this type.
|
|||
|
/// </returns>
|
|||
|
/// <exception cref="ArgumentNullException">type.</exception>
|
|||
|
public static Object? GetDefault(this Type type) {
|
|||
|
if(type == null) {
|
|||
|
throw new ArgumentNullException(nameof(type));
|
|||
|
}
|
|||
|
|
|||
|
return type.IsValueType ? Activator.CreateInstance(type) : default;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Determines whether [is i enumerable request].
|
|||
|
/// </summary>
|
|||
|
/// <param name="type">The type.</param>
|
|||
|
/// <returns>
|
|||
|
/// <c>true</c> if [is i enumerable request] [the specified type]; otherwise, <c>false</c>.
|
|||
|
/// </returns>
|
|||
|
/// <exception cref="ArgumentNullException">type.</exception>
|
|||
|
public static Boolean IsIEnumerable(this Type type) => type == null ? throw new ArgumentNullException(nameof(type)) : type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable<>);
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Tries to parse using the basic types.
|
|||
|
/// </summary>
|
|||
|
/// <param name="type">The type.</param>
|
|||
|
/// <param name="value">The value.</param>
|
|||
|
/// <param name="result">The result.</param>
|
|||
|
/// <returns>
|
|||
|
/// <c>true</c> if parsing was successful; otherwise, <c>false</c>.
|
|||
|
/// </returns>
|
|||
|
/// <exception cref="ArgumentNullException">type</exception>
|
|||
|
public static Boolean TryParseBasicType(this Type type, Object value, out Object? result) {
|
|||
|
if(type == null) {
|
|||
|
throw new ArgumentNullException(nameof(type));
|
|||
|
}
|
|||
|
|
|||
|
if(type != typeof(Boolean)) {
|
|||
|
return TryParseBasicType(type, value.ToStringInvariant(), out result);
|
|||
|
}
|
|||
|
|
|||
|
result = value.ToBoolean();
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Tries to parse using the basic types.
|
|||
|
/// </summary>
|
|||
|
/// <param name="type">The type.</param>
|
|||
|
/// <param name="value">The value.</param>
|
|||
|
/// <param name="result">The result.</param>
|
|||
|
/// <returns>
|
|||
|
/// <c>true</c> if parsing was successful; otherwise, <c>false</c>.
|
|||
|
/// </returns>
|
|||
|
/// <exception cref="ArgumentNullException">type</exception>
|
|||
|
public static Boolean TryParseBasicType(this Type type, String value, out Object? result) {
|
|||
|
if(type == null) {
|
|||
|
throw new ArgumentNullException(nameof(type));
|
|||
|
}
|
|||
|
|
|||
|
result = null;
|
|||
|
|
|||
|
return Definitions.BasicTypesInfo.Value.ContainsKey(type) && Definitions.BasicTypesInfo.Value[type].TryParse(value, out result);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Tries the type of the set basic value to a property.
|
|||
|
/// </summary>
|
|||
|
/// <param name="propertyInfo">The property information.</param>
|
|||
|
/// <param name="value">The value.</param>
|
|||
|
/// <param name="target">The object.</param>
|
|||
|
/// <returns>
|
|||
|
/// <c>true</c> if parsing was successful; otherwise, <c>false</c>.
|
|||
|
/// </returns>
|
|||
|
/// <exception cref="ArgumentNullException">propertyInfo.</exception>
|
|||
|
public static Boolean TrySetBasicType(this PropertyInfo propertyInfo, Object value, Object target) {
|
|||
|
if(propertyInfo == null) {
|
|||
|
throw new ArgumentNullException(nameof(propertyInfo));
|
|||
|
}
|
|||
|
|
|||
|
try {
|
|||
|
if(propertyInfo.PropertyType.TryParseBasicType(value, out Object? propertyValue)) {
|
|||
|
propertyInfo.SetValue(target, propertyValue);
|
|||
|
return true;
|
|||
|
}
|
|||
|
} catch {
|
|||
|
// swallow
|
|||
|
}
|
|||
|
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Tries the type of the set to an array a basic type.
|
|||
|
/// </summary>
|
|||
|
/// <param name="type">The type.</param>
|
|||
|
/// <param name="value">The value.</param>
|
|||
|
/// <param name="target">The array.</param>
|
|||
|
/// <param name="index">The index.</param>
|
|||
|
/// <returns>
|
|||
|
/// <c>true</c> if parsing was successful; otherwise, <c>false</c>.
|
|||
|
/// </returns>
|
|||
|
/// <exception cref="ArgumentNullException">type</exception>
|
|||
|
public static Boolean TrySetArrayBasicType(this Type type, Object value, Array target, Int32 index) {
|
|||
|
if(type == null) {
|
|||
|
throw new ArgumentNullException(nameof(type));
|
|||
|
}
|
|||
|
|
|||
|
if(target == null) {
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
try {
|
|||
|
if(value == null) {
|
|||
|
target.SetValue(null, index);
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
if(type.TryParseBasicType(value, out Object? propertyValue)) {
|
|||
|
target.SetValue(propertyValue, index);
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
if(type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) {
|
|||
|
target.SetValue(null, index);
|
|||
|
return true;
|
|||
|
}
|
|||
|
} catch {
|
|||
|
// swallow
|
|||
|
}
|
|||
|
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Tries to set a property array with another array.
|
|||
|
/// </summary>
|
|||
|
/// <param name="propertyInfo">The property.</param>
|
|||
|
/// <param name="value">The value.</param>
|
|||
|
/// <param name="obj">The object.</param>
|
|||
|
/// <returns>
|
|||
|
/// <c>true</c> if parsing was successful; otherwise, <c>false</c>.
|
|||
|
/// </returns>
|
|||
|
/// <exception cref="ArgumentNullException">propertyInfo.</exception>
|
|||
|
public static Boolean TrySetArray(this PropertyInfo propertyInfo, IEnumerable<Object>? value, Object obj) {
|
|||
|
if(propertyInfo == null) {
|
|||
|
throw new ArgumentNullException(nameof(propertyInfo));
|
|||
|
}
|
|||
|
|
|||
|
Type? elementType = propertyInfo.PropertyType.GetElementType();
|
|||
|
|
|||
|
if(elementType == null || value == null) {
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
Array targetArray = Array.CreateInstance(elementType, value.Count());
|
|||
|
|
|||
|
Int32 i = 0;
|
|||
|
|
|||
|
foreach(Object sourceElement in value) {
|
|||
|
Boolean result = elementType.TrySetArrayBasicType(sourceElement, targetArray, i++);
|
|||
|
|
|||
|
if(!result) {
|
|||
|
return false;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
propertyInfo.SetValue(obj, targetArray);
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Gets a MethodInfo from a Property Get method.
|
|||
|
/// </summary>
|
|||
|
/// <param name="propertyInfo">The property information.</param>
|
|||
|
/// <param name="nonPublic">if set to <c>true</c> [non public].</param>
|
|||
|
/// <returns>
|
|||
|
/// The cached MethodInfo.
|
|||
|
/// </returns>
|
|||
|
public static Func<Object, Object>? GetCacheGetMethod(this PropertyInfo propertyInfo, Boolean nonPublic = false) {
|
|||
|
Tuple<Boolean, PropertyInfo> key = Tuple.Create(!nonPublic, propertyInfo);
|
|||
|
|
|||
|
// TODO: Fix public logic
|
|||
|
return !nonPublic && !CacheGetMethods.Value.ContainsKey(key) && !propertyInfo.GetGetMethod(true)!.IsPublic ? null : CacheGetMethods.Value.GetOrAdd(key, x => y => x.Item2.GetGetMethod(nonPublic)?.Invoke(y, null)!);
|
|||
|
//y => x => y.Item2.CreatePropertyProxy().GetValue(x));
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Gets a MethodInfo from a Property Set method.
|
|||
|
/// </summary>
|
|||
|
/// <param name="propertyInfo">The property information.</param>
|
|||
|
/// <param name="nonPublic">if set to <c>true</c> [non public].</param>
|
|||
|
/// <returns>
|
|||
|
/// The cached MethodInfo.
|
|||
|
/// </returns>
|
|||
|
public static Action<Object, Object[]>? GetCacheSetMethod(this PropertyInfo propertyInfo, Boolean nonPublic = false) {
|
|||
|
Tuple<Boolean, PropertyInfo> key = Tuple.Create(!nonPublic, propertyInfo);
|
|||
|
|
|||
|
return !nonPublic && !CacheSetMethods.Value.ContainsKey(key) && !propertyInfo.GetSetMethod(true)!.IsPublic ? null : CacheSetMethods.Value.GetOrAdd(key, x => (obj, args) => x.Item2.GetSetMethod(nonPublic)!.Invoke(obj, args));
|
|||
|
//y => (obj, args) => y.Item2.CreatePropertyProxy().SetValue(obj, args));
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Convert a string to a boolean.
|
|||
|
/// </summary>
|
|||
|
/// <param name="str">The string.</param>
|
|||
|
/// <returns>
|
|||
|
/// <c>true</c> if the string represents a valid truly value, otherwise <c>false</c>.
|
|||
|
/// </returns>
|
|||
|
public static Boolean ToBoolean(this String str) {
|
|||
|
try {
|
|||
|
return Convert.ToBoolean(str);
|
|||
|
} catch(FormatException) {
|
|||
|
// ignored
|
|||
|
}
|
|||
|
|
|||
|
try {
|
|||
|
return Convert.ToBoolean(Convert.ToInt32(str));
|
|||
|
} catch {
|
|||
|
// ignored
|
|||
|
}
|
|||
|
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Convert a object to a boolean.
|
|||
|
/// </summary>
|
|||
|
/// <param name="value">The value.</param>
|
|||
|
/// <returns>
|
|||
|
/// <c>true</c> if the string represents a valid truly value, otherwise <c>false</c>.
|
|||
|
/// </returns>
|
|||
|
public static Boolean ToBoolean(this Object value) => value.ToStringInvariant().ToBoolean();
|
|||
|
}
|
|||
|
}
|