#nullable enable using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Reflection; using Swan.Configuration; using Swan.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 Assembly Extensions /*/// /// Gets all types within an assembly in a safe manner. /// /// The assembly. /// /// Array of Type objects representing the types specified by an assembly. /// /// assembly. public static IEnumerable GetAllTypes(this Assembly assembly) { if(assembly == null) { throw new ArgumentNullException(nameof(assembly)); } try { return assembly.GetTypes(); } catch(ReflectionTypeLoadException e) { return e.Types.Where(t => t != null); } }*/ #endregion #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 this type is compatible with ICollection. /// /// The type. /// /// true if the specified source type is collection; otherwise, false. /// /// sourceType. public static Boolean IsCollection(this Type sourceType) { if(sourceType == null) { throw new ArgumentNullException(nameof(sourceType)); } return sourceType != typeof(String) && typeof(IEnumerable).IsAssignableFrom(sourceType); } /// /// Gets a method from a type given the method name, binding flags, generic types and parameter types. /// /// Type of the source. /// The binding flags. /// Name of the method. /// The generic types. /// The parameter types. /// /// An object that represents the method with the specified name. /// /// /// The exception that is thrown when binding to a member results in more than one member matching the /// binding criteria. This class cannot be inherited. /// public static MethodInfo GetMethod(this Type type, BindingFlags bindingFlags, String methodName, Type[] genericTypes, Type[] parameterTypes) { if(type == null) { throw new ArgumentNullException(nameof(type)); } if(methodName == null) { throw new ArgumentNullException(nameof(methodName)); } if(genericTypes == null) { throw new ArgumentNullException(nameof(genericTypes)); } if(parameterTypes == null) { throw new ArgumentNullException(nameof(parameterTypes)); } List methods = type.GetMethods(bindingFlags) .Where(mi => String.Equals(methodName, mi.Name, StringComparison.Ordinal)) .Where(mi => mi.ContainsGenericParameters) .Where(mi => mi.GetGenericArguments().Length == genericTypes.Length) .Where(mi => mi.GetParameters().Length == parameterTypes.Length).Select(mi => mi.MakeGenericMethod(genericTypes)) .Where(mi => mi.GetParameters().Select(pi => pi.ParameterType).SequenceEqual(parameterTypes)).ToList(); return methods.Count > 1 ? throw new AmbiguousMatchException() : methods.FirstOrDefault(); }*/ /// /// 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 property actual value or PropertyDisplayAttribute.DefaultValue if presented. /// /// If the PropertyDisplayAttribute.Format value is presented, the property value /// will be formatted accordingly. /// /// If the object contains a null value, a empty string will be returned. /// /// The property information. /// The object. /// The property value or null. /// propertyInfo. public static String? ToFormattedString(this PropertyInfo propertyInfo, Object target) { if(propertyInfo == null) { throw new ArgumentNullException(nameof(propertyInfo)); } try { Object? value = propertyInfo.GetValue(target); PropertyDisplayAttribute attr = AttributeCache.DefaultCache.Value.RetrieveOne(propertyInfo); if(attr == null) { return value?.ToString() ?? String.Empty; } Object valueToFormat = value ?? attr.DefaultValue; return String.IsNullOrEmpty(attr.Format) ? (valueToFormat?.ToString() ?? String.Empty) : ConvertObjectAndFormat(propertyInfo.PropertyType, valueToFormat, attr.Format); } catch { return null; } }*/ /// /// 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; } /*/// /// Creates a property proxy that stores getter and setter delegates. /// /// The property information. /// /// The property proxy. /// /// this. public static IPropertyProxy? CreatePropertyProxy(this PropertyInfo @this) { if(@this == null) { throw new ArgumentNullException(nameof(@this)); } Type genericType = typeof(PropertyProxy<,>).MakeGenericType(@this.DeclaringType!, @this.PropertyType); return Activator.CreateInstance(genericType, @this) as IPropertyProxy; }*/ /// /// 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(); /*private static String ConvertObjectAndFormat(Type propertyType, Object value, String format) => propertyType == typeof(DateTime) || propertyType == typeof(DateTime?) ? Convert.ToDateTime(value, CultureInfo.InvariantCulture).ToString(format) : propertyType == typeof(Int32) || propertyType == typeof(Int32?) ? Convert.ToInt32(value, CultureInfo.InvariantCulture).ToString(format) : propertyType == typeof(Decimal) || propertyType == typeof(Decimal?) ? Convert.ToDecimal(value, CultureInfo.InvariantCulture).ToString(format) : propertyType == typeof(Double) || propertyType == typeof(Double?) ? Convert.ToDouble(value, CultureInfo.InvariantCulture).ToString(format) : propertyType == typeof(Byte) || propertyType == typeof(Byte?) ? Convert.ToByte(value, CultureInfo.InvariantCulture).ToString(format) : value?.ToString() ?? String.Empty;*/ } }