#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();
}
}