#nullable enable using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Reflection; namespace Swan { /// /// Provides various extension methods for Reflection and Types. /// public static class ReflectionExtensions { private static readonly Lazy, Func>> CacheGetMethods = new Lazy, Func>>(() => new ConcurrentDictionary, Func>(), true); private static readonly Lazy, Action>> CacheSetMethods = new Lazy, Action>>(() => new ConcurrentDictionary, Action>(), true); #region Type Extensions /// /// The closest programmatic equivalent of default(T). /// /// The type. /// /// Default value of this type. /// /// type. public static Object? GetDefault(this Type type) { if(type == null) { throw new ArgumentNullException(nameof(type)); } return type.IsValueType ? Activator.CreateInstance(type) : default; } /// /// Determines whether [is i enumerable request]. /// /// The type. /// /// true if [is i enumerable request] [the specified type]; otherwise, false. /// /// type. public static Boolean IsIEnumerable(this Type type) => type == null ? throw new ArgumentNullException(nameof(type)) : type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable<>); #endregion /// /// Tries to parse using the basic types. /// /// The type. /// The value. /// The result. /// /// true if parsing was successful; otherwise, false. /// /// type 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; } /// /// Tries to parse using the basic types. /// /// The type. /// The value. /// The result. /// /// true if parsing was successful; otherwise, false. /// /// type 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); } /// /// Tries the type of the set basic value to a property. /// /// The property information. /// The value. /// The object. /// /// true if parsing was successful; otherwise, false. /// /// propertyInfo. 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; } /// /// Tries the type of the set to an array a basic type. /// /// The type. /// The value. /// The array. /// The index. /// /// true if parsing was successful; otherwise, false. /// /// type 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; } /// /// Tries to set a property array with another array. /// /// The property. /// The value. /// The object. /// /// true if parsing was successful; otherwise, false. /// /// propertyInfo. public static Boolean TrySetArray(this PropertyInfo propertyInfo, IEnumerable? 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; } /// /// Gets a MethodInfo from a Property Get method. /// /// The property information. /// if set to true [non public]. /// /// The cached MethodInfo. /// public static Func? GetCacheGetMethod(this PropertyInfo propertyInfo, Boolean nonPublic = false) { Tuple 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)); } /// /// Gets a MethodInfo from a Property Set method. /// /// The property information. /// if set to true [non public]. /// /// The cached MethodInfo. /// public static Action? GetCacheSetMethod(this PropertyInfo propertyInfo, Boolean nonPublic = false) { Tuple 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)); } /// /// Convert a string to a boolean. /// /// The string. /// /// true if the string represents a valid truly value, otherwise false. /// 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; } /// /// Convert a object to a boolean. /// /// The value. /// /// true if the string represents a valid truly value, otherwise false. /// public static Boolean ToBoolean(this Object value) => value.ToStringInvariant().ToBoolean(); } }